mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 12:46:00 +02:00
Compare commits
53 Commits
3.14.0-b1
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b45dda8ada | ||
|
|
e1577d8e0c | ||
|
|
a184a7cc7f | ||
|
|
6a4ca77009 | ||
|
|
c1ed748a91 | ||
|
|
d6791a9c89 | ||
|
|
1f0411fb3d | ||
|
|
27a1838980 | ||
|
|
c1bfb482e1 | ||
|
|
411e012f5c | ||
|
|
a7bc32d7ae | ||
|
|
ef3f69ece1 | ||
|
|
344f4905fc | ||
|
|
51f36d14ec | ||
|
|
f2b31b2ac4 | ||
|
|
118734d7fb | ||
|
|
d7b7f6571b | ||
|
|
9728843b0a | ||
|
|
033ac70c8a | ||
|
|
85247e8307 | ||
|
|
d38a896685 | ||
|
|
880908c163 | ||
|
|
ec94d5ef46 | ||
|
|
49f9863922 | ||
|
|
6d4249cf1e | ||
|
|
0fc0ef1d3e | ||
|
|
c587f2bcdc | ||
|
|
5cd8c68589 | ||
|
|
61690db0ee | ||
|
|
fcc2b6b6a8 | ||
|
|
a5412cd2b3 | ||
|
|
a47a7417c2 | ||
|
|
bdac77e88c | ||
|
|
8ab79a2937 | ||
|
|
8dc880577f | ||
|
|
b1829651d3 | ||
|
|
7339872de6 | ||
|
|
8155643098 | ||
|
|
87a1cde859 | ||
|
|
a591d31d01 | ||
|
|
e863c41117 | ||
|
|
811539ae19 | ||
|
|
bc7327a6d5 | ||
|
|
0c114964f2 | ||
|
|
b8a3e5e4c1 | ||
|
|
1d16e9e220 | ||
|
|
c96b464cbc | ||
|
|
43821e6f50 | ||
|
|
e0e642db5a | ||
|
|
2266fd4d3d | ||
|
|
6b8145c674 | ||
|
|
fa89ab8608 | ||
|
|
da70b0fb18 |
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018-2025 Dmitriy Yefremov
|
Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,9 +1,38 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018-2026 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 <https://github.com/DYefremov>
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from threading import Thread, Timer
|
from threading import Timer
|
||||||
|
|
||||||
from gi.repository import GLib
|
from gi.repository import GLib
|
||||||
|
from gi.repository.Gio import Task
|
||||||
|
|
||||||
_LOG_FILE = "demon-editor.log"
|
_LOG_FILE = "demon-editor.log"
|
||||||
LOG_DATE_FORMAT = "%d-%m-%y %H:%M:%S"
|
LOG_DATE_FORMAT = "%d-%m-%y %H:%M:%S"
|
||||||
@@ -41,12 +70,12 @@ def run_idle(func):
|
|||||||
|
|
||||||
|
|
||||||
def run_task(func):
|
def run_task(func):
|
||||||
""" Runs function in separate thread """
|
""" Runs a function in a separate thread. """
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
task = Thread(target=func, args=args, kwargs=kwargs, daemon=True)
|
task = Task()
|
||||||
task.start()
|
task.run_in_thread(lambda t, s, d, c: func(*args, **kwargs))
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ Terrestrial = namedtuple("Terrestrial", ["name", "flags", "countrycode", "transp
|
|||||||
Cable = namedtuple("Cable", ["name", "flags", "satfeed", "countrycode", "transponders"])
|
Cable = namedtuple("Cable", ["name", "flags", "satfeed", "countrycode", "transponders"])
|
||||||
|
|
||||||
Transponder = namedtuple("Transponder", ["frequency", "symbol_rate", "polarization", "fec_inner", "system",
|
Transponder = namedtuple("Transponder", ["frequency", "symbol_rate", "polarization", "fec_inner", "system",
|
||||||
"modulation", "pls_mode", "pls_code", "is_id", "t2mi_plp_id"])
|
"modulation", "pls_mode", "pls_code", "is_id", "t2mi_plp_id", "t2mi_pid"])
|
||||||
TerTransponder = namedtuple("TerTransponder", ["centre_frequency", "system", "bandwidth", "constellation",
|
TerTransponder = namedtuple("TerTransponder", ["centre_frequency", "system", "bandwidth", "constellation",
|
||||||
"code_rate_hp", "code_rate_lp", "guard_interval", "transmission_mode",
|
"code_rate_hp", "code_rate_lp", "guard_interval", "transmission_mode",
|
||||||
"hierarchy_information", "inversion", "plp_id"])
|
"hierarchy_information", "inversion", "plp_id"])
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2023 Dmitriy Yefremov
|
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -88,7 +88,8 @@ def get_sat_transponders(elem):
|
|||||||
e.get("pls_mode", None),
|
e.get("pls_mode", None),
|
||||||
e.get("pls_code", None),
|
e.get("pls_code", None),
|
||||||
e.get("is_id", None),
|
e.get("is_id", None),
|
||||||
e.get("t2mi_plp_id", None)) for e in elem.iter("transponder")]
|
e.get("t2mi_plp_id", None),
|
||||||
|
e.get("t2mi_pid", None)) for e in elem.iter("transponder")]
|
||||||
|
|
||||||
|
|
||||||
def get_terrestrial(path):
|
def get_terrestrial(path):
|
||||||
|
|||||||
102
app/tools/epg.py
102
app/tools/epg.py
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -257,62 +257,64 @@ class XmlTvReader(Reader):
|
|||||||
log(f"{self.__class__.__name__} [download] error: Invalid URL {self._url}")
|
log(f"{self.__class__.__name__} [download] error: Invalid URL {self._url}")
|
||||||
return
|
return
|
||||||
|
|
||||||
with requests.get(url=self._url, stream=True) as resp:
|
try:
|
||||||
if resp.reason == "OK":
|
with requests.get(url=self._url, stream=True, timeout=(5, 5)) as resp:
|
||||||
suf = self._url[self._url.rfind("."):]
|
if resp.reason == "OK":
|
||||||
if suf not in self.SUFFIXES:
|
suf = self._url[self._url.rfind("."):]
|
||||||
log(f"{self.__class__.__name__} [download] error: Unsupported file extension.")
|
if suf not in self.SUFFIXES:
|
||||||
return
|
log(f"{self.__class__.__name__} [download] error: Unsupported file extension.")
|
||||||
|
return
|
||||||
|
|
||||||
data_size = resp.headers.get("content-length")
|
data_size = resp.headers.get("content-length")
|
||||||
if not data_size:
|
if not data_size:
|
||||||
log(f"{self.__class__.__name__} [download *.{suf}] error: Error getting data size.")
|
log(f"{self.__class__.__name__} [download *.{suf}] error: Error getting data size.")
|
||||||
if clb:
|
return
|
||||||
clb()
|
|
||||||
return
|
|
||||||
|
|
||||||
with NamedTemporaryFile(suffix=suf, delete=not IS_WIN) as tf:
|
with NamedTemporaryFile(suffix=suf, delete=not IS_WIN) as tf:
|
||||||
downloaded = 0
|
downloaded = 0
|
||||||
data_size = int(data_size)
|
data_size = int(data_size)
|
||||||
completed = set()
|
completed = set()
|
||||||
|
|
||||||
for data in resp.iter_content(chunk_size=1024):
|
for data in resp.iter_content(chunk_size=128):
|
||||||
downloaded += len(data)
|
downloaded += len(data)
|
||||||
tf.write(data)
|
tf.write(data)
|
||||||
done = int(100 * downloaded / data_size)
|
done = int(100 * downloaded / data_size)
|
||||||
if done % 25 == 0 and done not in completed:
|
if done % 25 == 0 and done not in completed:
|
||||||
completed.add(done)
|
completed.add(done)
|
||||||
log(f"Downloading XMLTV file...{done}%" if done < 100 else "XMLTV file download complete.")
|
log(f"Downloading XMLTV file...{done}%" if done < 100 else "XMLTV file download complete.")
|
||||||
tf.seek(0)
|
tf.seek(0)
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(self._path), exist_ok=True)
|
os.makedirs(os.path.dirname(self._path), exist_ok=True)
|
||||||
|
|
||||||
if suf.endswith(".gz"):
|
if suf.endswith(".gz"):
|
||||||
try:
|
try:
|
||||||
shutil.copyfile(tf.name, self._path)
|
shutil.copyfile(tf.name, self._path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
log(f"{self.__class__.__name__} [download *.gz] error: {e}")
|
log(f"{self.__class__.__name__} [download *.gz] error: {e}")
|
||||||
elif self._url.endswith((".xz", ".lzma")):
|
elif self._url.endswith((".xz", ".lzma")):
|
||||||
import lzma
|
import lzma
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with lzma.open(tf, "rb") as lzf:
|
with lzma.open(tf, "rb") as lzf:
|
||||||
shutil.copyfileobj(lzf, self._path)
|
shutil.copyfileobj(lzf, self._path)
|
||||||
except (lzma.LZMAError, OSError) as e:
|
except (lzma.LZMAError, OSError) as e:
|
||||||
log(f"{self.__class__.__name__} [download *.xz] error: {e}")
|
log(f"{self.__class__.__name__} [download *.xz] error: {e}")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
import gzip
|
import gzip
|
||||||
with gzip.open(self._path, "wb") as f_out:
|
with gzip.open(self._path, "wb") as f_out:
|
||||||
shutil.copyfileobj(tf, f_out)
|
shutil.copyfileobj(tf, f_out)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
log(f"{self.__class__.__name__} [download *.xml] error: {e}")
|
log(f"{self.__class__.__name__} [download *.xml] error: {e}")
|
||||||
|
|
||||||
if IS_WIN and os.path.isfile(tf.name):
|
if IS_WIN and os.path.isfile(tf.name):
|
||||||
tf.close()
|
tf.close()
|
||||||
os.remove(tf.name)
|
os.remove(tf.name)
|
||||||
else:
|
else:
|
||||||
log(f"{self.__class__.__name__} [download] error: {resp.reason}")
|
log(f"{self.__class__.__name__} [download] error: {resp.reason}")
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
log(f"{self.__class__.__name__} [download] error: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
if clb:
|
if clb:
|
||||||
clb()
|
clb()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2024 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -31,9 +31,13 @@ import os
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import tempfile
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
from datetime import datetime
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
|
from io import BytesIO
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
@@ -74,46 +78,76 @@ class PiconsCzDownloader:
|
|||||||
self._provider_logos = {}
|
self._provider_logos = {}
|
||||||
self._picon_ids = picon_ids
|
self._picon_ids = picon_ids
|
||||||
self._appender = appender
|
self._appender = appender
|
||||||
|
self._logo_map = self.get_logos_map()
|
||||||
|
self._name_map = self.get_name_map()
|
||||||
|
self._perm_cache_file = Path(tempfile.gettempdir()).joinpath("picon_cz_links")
|
||||||
|
# subprocess creation flags
|
||||||
|
self._sbp_flags = subprocess.CREATE_NO_WINDOW if IS_WIN else 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def providers(self):
|
||||||
|
return self._providers
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
""" Initializes dict with values: download_id -> perm link and provider data. """
|
""" Initializes dict with values: download_id -> perm link and provider data. """
|
||||||
if self._perm_links:
|
if self._perm_links:
|
||||||
return
|
return
|
||||||
|
|
||||||
self._HEADER["Referer"] = self._PERM_URL
|
if self._perm_cache_file.exists():
|
||||||
|
st = self._perm_cache_file.stat()
|
||||||
|
dif = datetime.now() - datetime.fromtimestamp(st.st_mtime)
|
||||||
|
# We will update daily.
|
||||||
|
if dif.days > 0:
|
||||||
|
self.download_permalinks()
|
||||||
|
else:
|
||||||
|
self.download_permalinks()
|
||||||
|
|
||||||
|
self.read_permalinks()
|
||||||
|
|
||||||
|
def download_permalinks(self):
|
||||||
|
self._HEADER["Referer"] = self._PERM_URL
|
||||||
with requests.get(url=self._PERM_URL, headers=self._HEADER, stream=True) as request:
|
with requests.get(url=self._PERM_URL, headers=self._HEADER, stream=True) as request:
|
||||||
if request.reason == "OK":
|
if request.reason == "OK":
|
||||||
logo_map = self.get_logos_map()
|
log(f"{self.__class__.__name__}: downloading permalinks file...")
|
||||||
name_map = self.get_name_map()
|
buf = BytesIO()
|
||||||
|
[buf.write(chunk) for chunk in request.iter_content(chunk_size=128)]
|
||||||
|
buf.seek(0)
|
||||||
|
|
||||||
for line in request.iter_lines():
|
self._perm_cache_file.touch()
|
||||||
data = line.decode(encoding="utf-8", errors="ignore").split(maxsplit=1)
|
self._perm_cache_file.write_bytes(buf.read())
|
||||||
if len(data) != 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
l_id, perm_link = data
|
|
||||||
self._perm_links[str(l_id)] = str(perm_link)
|
|
||||||
data = re.match(self._LINK_PATTERN, perm_link)
|
|
||||||
if data:
|
|
||||||
sat_pos = data.group(3)
|
|
||||||
# Logo url.
|
|
||||||
logo = logo_map.get(data.group(2), None)
|
|
||||||
l_name = name_map.get(sat_pos, None) or sat_pos.replace(".", "")
|
|
||||||
logo_url = f"{self._BASE_LOGO_URL}{logo}/{l_name}.png" if logo else None
|
|
||||||
|
|
||||||
prv = Provider(None, data.group(1), sat_pos, self._BASE_URL + l_id, l_id, logo_url, None, False)
|
|
||||||
if sat_pos in self._providers:
|
|
||||||
self._providers[sat_pos].append(prv)
|
|
||||||
else:
|
|
||||||
self._providers[sat_pos] = [prv]
|
|
||||||
else:
|
else:
|
||||||
log(f"{self.__class__.__name__} [get permalinks] error: {request.reason}")
|
log(f"{self.__class__.__name__} [get permalinks] error: {request.reason}")
|
||||||
raise PiconsError(request.reason)
|
raise PiconsError(request.reason)
|
||||||
|
|
||||||
@property
|
def read_permalinks(self):
|
||||||
def providers(self):
|
with self._perm_cache_file.open(encoding="utf-8", errors="ignore") as f:
|
||||||
return self._providers
|
for l in f.readlines():
|
||||||
|
data = l.split(maxsplit=1)
|
||||||
|
if len(data) != 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
data = l.split(maxsplit=1)
|
||||||
|
if len(data) != 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
l_id, perm_link = data
|
||||||
|
self._perm_links[str(l_id)] = str(perm_link)
|
||||||
|
self.update_provider_data(l_id, perm_link)
|
||||||
|
|
||||||
|
def update_provider_data(self, l_id, perm_link):
|
||||||
|
data = re.match(self._LINK_PATTERN, perm_link)
|
||||||
|
if data:
|
||||||
|
sat_pos = data.group(3)
|
||||||
|
# Logo url.
|
||||||
|
logo = self._logo_map.get(data.group(2), None)
|
||||||
|
l_name = self._name_map.get(sat_pos, None) or sat_pos.replace(".", "")
|
||||||
|
logo_url = f"{self._BASE_LOGO_URL}{logo}/{l_name}.png" if logo else None
|
||||||
|
|
||||||
|
prv = Provider(None, data.group(1), sat_pos, self._BASE_URL + l_id, l_id, logo_url, None, False)
|
||||||
|
if sat_pos in self._providers:
|
||||||
|
self._providers[sat_pos].append(prv)
|
||||||
|
else:
|
||||||
|
self._providers[sat_pos] = [prv]
|
||||||
|
|
||||||
def get_sat_providers(self, url):
|
def get_sat_providers(self, url):
|
||||||
return self._providers.get(url, [])
|
return self._providers.get(url, [])
|
||||||
@@ -149,7 +183,10 @@ class PiconsCzDownloader:
|
|||||||
|
|
||||||
cmd = [exe, "l", src]
|
cmd = [exe, "l", src]
|
||||||
try:
|
try:
|
||||||
out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
out, err = subprocess.Popen(cmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
creationflags=self._sbp_flags).communicate()
|
||||||
if err:
|
if err:
|
||||||
log(f"{self.__class__.__name__} [extract] error: {err}")
|
log(f"{self.__class__.__name__} [extract] error: {err}")
|
||||||
raise PiconsError(err)
|
raise PiconsError(err)
|
||||||
@@ -174,7 +211,10 @@ class PiconsCzDownloader:
|
|||||||
cmd = [exe, "e", src, "-o{}".format(dest), "-y", "-r"]
|
cmd = [exe, "e", src, "-o{}".format(dest), "-y", "-r"]
|
||||||
cmd.extend(to_extract)
|
cmd.extend(to_extract)
|
||||||
try:
|
try:
|
||||||
out, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
out, err = subprocess.Popen(cmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
creationflags=self._sbp_flags).communicate()
|
||||||
if err:
|
if err:
|
||||||
log(f"{self.__class__.__name__} [extract] error: {err}")
|
log(f"{self.__class__.__name__} [extract] error: {err}")
|
||||||
raise PiconsError(err)
|
raise PiconsError(err)
|
||||||
@@ -207,7 +247,8 @@ class PiconsCzDownloader:
|
|||||||
except requests.exceptions.ConnectionError as e:
|
except requests.exceptions.ConnectionError as e:
|
||||||
log(f"{self.__class__.__name__} error [get provider logo]: {e}")
|
log(f"{self.__class__.__name__} error [get provider logo]: {e}")
|
||||||
|
|
||||||
def get_logos_map(self):
|
@staticmethod
|
||||||
|
def get_logos_map():
|
||||||
return {"piconblack": "b50",
|
return {"piconblack": "b50",
|
||||||
"picontransparent": "t50",
|
"picontransparent": "t50",
|
||||||
"piconwhite": "w50",
|
"piconwhite": "w50",
|
||||||
@@ -232,7 +273,8 @@ class PiconsCzDownloader:
|
|||||||
"piconSNPblack": "b50",
|
"piconSNPblack": "b50",
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_name_map(self):
|
@staticmethod
|
||||||
|
def get_name_map():
|
||||||
return {"antiksat": "ANTIK",
|
return {"antiksat": "ANTIK",
|
||||||
"digiczsk": "DIGI",
|
"digiczsk": "DIGI",
|
||||||
"DTTitaly": "picon_trs-it",
|
"DTTitaly": "picon_trs-it",
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ class SatellitesParser(HTMLParser):
|
|||||||
self.FEC.get(fec, None),
|
self.FEC.get(fec, None),
|
||||||
self.SYSTEM.get(sys, None),
|
self.SYSTEM.get(sys, None),
|
||||||
self.MODULATION.get(mod, None),
|
self.MODULATION.get(mod, None),
|
||||||
pls_mode, pls_code, None, None)
|
pls_mode, pls_code, None, None, None)
|
||||||
if is_transponder_valid(tr):
|
if is_transponder_valid(tr):
|
||||||
trs.append(tr)
|
trs.append(tr)
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ class SatellitesParser(HTMLParser):
|
|||||||
self.FEC.get(fec, None),
|
self.FEC.get(fec, None),
|
||||||
self.SYSTEM.get(sys, None),
|
self.SYSTEM.get(sys, None),
|
||||||
self.MODULATION.get(mod, None),
|
self.MODULATION.get(mod, None),
|
||||||
pls_mode, pls_code, is_id, None)
|
pls_mode, pls_code, is_id, None, None)
|
||||||
if is_transponder_valid(tr):
|
if is_transponder_valid(tr):
|
||||||
trs.append(tr)
|
trs.append(tr)
|
||||||
|
|
||||||
@@ -421,7 +421,7 @@ class SatellitesParser(HTMLParser):
|
|||||||
self.FEC.get(fec, None),
|
self.FEC.get(fec, None),
|
||||||
self.SYSTEM.get(sys, None),
|
self.SYSTEM.get(sys, None),
|
||||||
self.MODULATION.get(mod, None),
|
self.MODULATION.get(mod, None),
|
||||||
pls_id, pls_code, is_id, None)
|
pls_id, pls_code, is_id, None, None)
|
||||||
if is_transponder_valid(tr):
|
if is_transponder_valid(tr):
|
||||||
trs.append(tr)
|
trs.append(tr)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2024 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -223,11 +223,11 @@ class BackupDialog:
|
|||||||
self._settings.add("backup_tool_window_size", window.get_size())
|
self._settings.add("backup_tool_window_size", window.get_size())
|
||||||
|
|
||||||
def on_key_release(self, view, event):
|
def on_key_release(self, view, event):
|
||||||
""" Handling keystrokes """
|
""" Handling keystrokes. """
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
|
|
||||||
if key is KeyboardKey.DELETE:
|
if key is KeyboardKey.DELETE:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -37,7 +37,7 @@ from gi.repository.GObject import BindingFlags
|
|||||||
|
|
||||||
from app.commons import log, run_task
|
from app.commons import log, run_task
|
||||||
from app.connections import UtfFTP
|
from app.connections import UtfFTP
|
||||||
from app.settings import IS_DARWIN
|
from app.settings import IS_DARWIN, IS_WIN
|
||||||
from app.ui.dialogs import translate, get_chooser_dialog, show_dialog, DialogType
|
from app.ui.dialogs import translate, get_chooser_dialog, show_dialog, DialogType
|
||||||
from app.ui.main_helper import get_picon_pixbuf, redraw_image
|
from app.ui.main_helper import get_picon_pixbuf, redraw_image
|
||||||
from app.ui.uicommons import HeaderBar
|
from app.ui.uicommons import HeaderBar
|
||||||
@@ -64,6 +64,8 @@ class BootLogoManager(Gtk.Window):
|
|||||||
self._exe = f"{'./' if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS') else ''}ffmpeg"
|
self._exe = f"{'./' if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS') else ''}ffmpeg"
|
||||||
self._pix = None
|
self._pix = None
|
||||||
self._img_path = None
|
self._img_path = None
|
||||||
|
# subprocess creation flags
|
||||||
|
self._sbp_flags = subprocess.CREATE_NO_WINDOW if IS_WIN else 0
|
||||||
|
|
||||||
margin = {"margin_start": 5, "margin_end": 5, "margin_top": 5, "margin_bottom": 5}
|
margin = {"margin_start": 5, "margin_end": 5, "margin_top": 5, "margin_bottom": 5}
|
||||||
base_margin = {"margin_start": 10, "margin_end": 10, "margin_top": 10, "margin_bottom": 10}
|
base_margin = {"margin_start": 10, "margin_end": 10, "margin_top": 10, "margin_bottom": 10}
|
||||||
@@ -209,7 +211,9 @@ class BootLogoManager(Gtk.Window):
|
|||||||
def init(self, *args):
|
def init(self, *args):
|
||||||
log(f"{self.__class__.__name__} [init] Checking FFmpeg...")
|
log(f"{self.__class__.__name__} [init] Checking FFmpeg...")
|
||||||
try:
|
try:
|
||||||
out = subprocess.check_output([self._exe, "-version"], stderr=subprocess.STDOUT)
|
out = subprocess.check_output([self._exe, "-version"],
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
creationflags=self._sbp_flags)
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
msg = translate("Check if FFmpeg is installed!")
|
msg = translate("Check if FFmpeg is installed!")
|
||||||
self._app.show_error_message(f"Error. {e} {msg}")
|
self._app.show_error_message(f"Error. {e} {msg}")
|
||||||
@@ -297,6 +301,11 @@ class BootLogoManager(Gtk.Window):
|
|||||||
return
|
return
|
||||||
|
|
||||||
output = path.parent.joinpath(self._file_combo_box.get_active_id())
|
output = path.parent.joinpath(self._file_combo_box.get_active_id())
|
||||||
|
if Path(output).exists():
|
||||||
|
msg = f"\n{translate('The file already exists!')}\n\n\t{translate('Are you sure?')}"
|
||||||
|
if show_dialog(DialogType.QUESTION, self, msg) != Gtk.ResponseType.OK:
|
||||||
|
return True
|
||||||
|
|
||||||
ffmpeg_output = path.parent.joinpath(f"{self._file_combo_box.get_active_text()}.m2v")
|
ffmpeg_output = path.parent.joinpath(f"{self._file_combo_box.get_active_text()}.m2v")
|
||||||
|
|
||||||
cmd = [self._exe,
|
cmd = [self._exe,
|
||||||
@@ -322,9 +331,9 @@ class BootLogoManager(Gtk.Window):
|
|||||||
|
|
||||||
# Processing image.
|
# Processing image.
|
||||||
log(f"{self.__class__.__name__} [convert] Converting...")
|
log(f"{self.__class__.__name__} [convert] Converting...")
|
||||||
subprocess.run(cmd)
|
subprocess.run(cmd, creationflags=self._sbp_flags)
|
||||||
if Path(ffmpeg_output).exists():
|
if Path(ffmpeg_output).exists():
|
||||||
os.rename(ffmpeg_output, output)
|
os.replace(ffmpeg_output, output)
|
||||||
log(f"{self.__class__.__name__} [convert] -> '{output}'. Done!")
|
log(f"{self.__class__.__name__} [convert] -> '{output}'. Done!")
|
||||||
|
|
||||||
if cmd[2] != self._img_path:
|
if cmd[2] != self._img_path:
|
||||||
@@ -336,7 +345,7 @@ class BootLogoManager(Gtk.Window):
|
|||||||
|
|
||||||
def convert_to_image(self, video_path, img_path):
|
def convert_to_image(self, video_path, img_path):
|
||||||
cmd = [self._exe, "-y", "-i", video_path, img_path]
|
cmd = [self._exe, "-y", "-i", video_path, img_path]
|
||||||
subprocess.run(cmd)
|
subprocess.run(cmd, creationflags=self._sbp_flags)
|
||||||
|
|
||||||
@run_task
|
@run_task
|
||||||
def download_data(self, f_name):
|
def download_data(self, f_name):
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2018-2024 Dmitriy Yefremov
|
Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -31,7 +31,7 @@ Author: Dmitriy Yefremov
|
|||||||
<!-- interface-license-type mit -->
|
<!-- interface-license-type mit -->
|
||||||
<!-- interface-name DemonEditor -->
|
<!-- interface-name DemonEditor -->
|
||||||
<!-- interface-description Enigma2 channel and satellites list editor. -->
|
<!-- interface-description Enigma2 channel and satellites list editor. -->
|
||||||
<!-- interface-copyright 2018-2025 Dmitriy Yefremov -->
|
<!-- interface-copyright 2018-2026 Dmitriy Yefremov -->
|
||||||
<!-- interface-authors Dmitriy Yefremov -->
|
<!-- interface-authors Dmitriy Yefremov -->
|
||||||
<object class="GtkAboutDialog" id="about_dialog">
|
<object class="GtkAboutDialog" id="about_dialog">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
@@ -40,8 +40,8 @@ Author: Dmitriy Yefremov
|
|||||||
<property name="icon_name">system-help</property>
|
<property name="icon_name">system-help</property>
|
||||||
<property name="type_hint">normal</property>
|
<property name="type_hint">normal</property>
|
||||||
<property name="program_name">DemonEditor</property>
|
<property name="program_name">DemonEditor</property>
|
||||||
<property name="version">3.14.0 Beta</property>
|
<property name="version">3.14.4 Beta</property>
|
||||||
<property name="copyright">2018-2025 Dmitriy Yefremov
|
<property name="copyright">2018-2026 Dmitriy Yefremov
|
||||||
</property>
|
</property>
|
||||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor.</property>
|
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor.</property>
|
||||||
<property name="website">https://dyefremov.github.io/DemonEditor/</property>
|
<property name="website">https://dyefremov.github.io/DemonEditor/</property>
|
||||||
@@ -158,6 +158,7 @@ Author: Dmitriy Yefremov
|
|||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="resizable">False</property>
|
<property name="resizable">False</property>
|
||||||
<property name="modal">True</property>
|
<property name="modal">True</property>
|
||||||
|
<property name="width-request">170</property>
|
||||||
<property name="window_position">center-on-parent</property>
|
<property name="window_position">center-on-parent</property>
|
||||||
<property name="destroy_with_parent">True</property>
|
<property name="destroy_with_parent">True</property>
|
||||||
<property name="type_hint">splashscreen</property>
|
<property name="type_hint">splashscreen</property>
|
||||||
@@ -166,19 +167,19 @@ Author: Dmitriy Yefremov
|
|||||||
<property name="decorated">False</property>
|
<property name="decorated">False</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox" id="wait_dialog_box">
|
<object class="GtkBox" id="wait_dialog_box">
|
||||||
<property name="width_request">100</property>
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="margin_top">5</property>
|
<property name="margin-top">10</property>
|
||||||
<property name="margin_bottom">5</property>
|
<property name="margin-bottom">10</property>
|
||||||
|
<property name="margin-start">10</property>
|
||||||
|
<property name="margin_end">10</property>
|
||||||
<property name="orientation">vertical</property>
|
<property name="orientation">vertical</property>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSpinner" id="spinner">
|
<object class="LoadingProgressBar" id="progress">
|
||||||
<property name="width_request">150</property>
|
<property name="visible" bind-source="wait_dialog" bind-property="visible">True</property>
|
||||||
<property name="height_request">45</property>
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="active">True</property>
|
<property name="show-text">True</property>
|
||||||
|
<property name="text" translatable="yes">Loading data...</property>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
@@ -186,24 +187,10 @@ Author: Dmitriy Yefremov
|
|||||||
<property name="position">0</property>
|
<property name="position">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="GtkLabel" id="wait_dialog_label">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="margin_left">10</property>
|
|
||||||
<property name="margin_right">10</property>
|
|
||||||
<property name="label" translatable="yes">Loading data...</property>
|
|
||||||
</object>
|
|
||||||
<packing>
|
|
||||||
<property name="expand">False</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
<property name="position">1</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child> <!-- NOP -->
|
</child> <!-- NOP -->
|
||||||
<style>
|
<style>
|
||||||
<class name="app-notification"/>
|
<class name="primary-toolbar"/>
|
||||||
</style>
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2024 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -34,7 +34,7 @@ from functools import lru_cache
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from app.commons import run_idle
|
from app.commons import run_idle
|
||||||
from app.settings import SEP, IS_WIN, USE_HEADER_BAR
|
from app.settings import SEP, USE_HEADER_BAR, IS_LINUX
|
||||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||||
|
|
||||||
|
|
||||||
@@ -101,8 +101,8 @@ class WaitDialog:
|
|||||||
builder, dialog = get_dialog_from_xml(DialogType.WAIT, transient)
|
builder, dialog = get_dialog_from_xml(DialogType.WAIT, transient)
|
||||||
self._dialog = dialog
|
self._dialog = dialog
|
||||||
self._dialog.set_transient_for(transient)
|
self._dialog.set_transient_for(transient)
|
||||||
self._label = builder.get_object("wait_dialog_label")
|
self._progress = builder.get_object("progress")
|
||||||
self._default_text = text or self._label.get_text()
|
self._default_text = text or self._progress.get_text()
|
||||||
|
|
||||||
def show(self, text=None):
|
def show(self, text=None):
|
||||||
self.set_text(text)
|
self.set_text(text)
|
||||||
@@ -110,7 +110,7 @@ class WaitDialog:
|
|||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def set_text(self, text):
|
def set_text(self, text):
|
||||||
self._label.set_text(translate(text or self._default_text))
|
self._progress.set_text(translate(text or self._default_text))
|
||||||
|
|
||||||
@run_idle
|
@run_idle
|
||||||
def hide(self):
|
def hide(self):
|
||||||
@@ -228,7 +228,7 @@ def translate(message):
|
|||||||
|
|
||||||
@lru_cache(maxsize=5)
|
@lru_cache(maxsize=5)
|
||||||
def get_dialogs_string(path, tag="property"):
|
def get_dialogs_string(path, tag="property"):
|
||||||
if IS_WIN:
|
if not IS_LINUX:
|
||||||
return translate_xml(path, tag)
|
return translate_xml(path, tag)
|
||||||
else:
|
else:
|
||||||
with open(path, "r", encoding="utf-8") as f:
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
@@ -257,7 +257,7 @@ def get_builder(path, handlers=None, use_str=False, objects=None, tag="property"
|
|||||||
|
|
||||||
|
|
||||||
def translate_xml(path, tag="property"):
|
def translate_xml(path, tag="property"):
|
||||||
""" Used to translate GUI from * .glade files in MS Windows.
|
""" Used to translate GUI from *.glade files to macOS and MS Windows.
|
||||||
|
|
||||||
More info: https://gitlab.gnome.org/GNOME/gtk/-/issues/569
|
More info: https://gitlab.gnome.org/GNOME/gtk/-/issues/569
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -568,6 +568,7 @@ class TabEpgSettingsPopover(EpgSettingsPopover):
|
|||||||
self._url_combo_box.get_model().clear()
|
self._url_combo_box.get_model().clear()
|
||||||
[self._url_combo_box.append(i, i) for i in settings.epg_xml_sources if i]
|
[self._url_combo_box.append(i, i) for i in settings.epg_xml_sources if i]
|
||||||
self._url_combo_box.set_active_id(settings.epg_xml_source)
|
self._url_combo_box.set_active_id(settings.epg_xml_source)
|
||||||
|
self._remove_url_button.set_sensitive(len(self._url_combo_box.get_model()) > 1)
|
||||||
|
|
||||||
def on_apply(self, button):
|
def on_apply(self, button):
|
||||||
settings = self._app.app_settings
|
settings = self._app.app_settings
|
||||||
@@ -1217,10 +1218,10 @@ class EpgDialog:
|
|||||||
|
|
||||||
def on_key_press(self, view, event):
|
def on_key_press(self, view, event):
|
||||||
""" Handling keystrokes """
|
""" Handling keystrokes """
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
ctrl = event.state & Gdk.ModifierType.CONTROL_MASK
|
||||||
|
|
||||||
if ctrl and key is KeyboardKey.C:
|
if ctrl and key is KeyboardKey.C:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2023-2024 Dmitriy Yefremov
|
# Copyright (c) 2023-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -35,7 +35,7 @@ import requests
|
|||||||
from gi.repository import Gtk, Gdk, GLib, Pango, GObject
|
from gi.repository import Gtk, Gdk, GLib, Pango, GObject
|
||||||
|
|
||||||
from app.commons import log, run_task, run_idle
|
from app.commons import log, run_task, run_idle
|
||||||
from app.ui.dialogs import translate
|
from app.ui.dialogs import translate, show_dialog, DialogType
|
||||||
from app.ui.uicommons import HeaderBar
|
from app.ui.uicommons import HeaderBar
|
||||||
|
|
||||||
EXT_URL = "https://api.github.com/repos/DYefremov/demoneditor-extensions/contents/extensions/"
|
EXT_URL = "https://api.github.com/repos/DYefremov/demoneditor-extensions/contents/extensions/"
|
||||||
@@ -48,7 +48,7 @@ HEADERS = {"User-Agent": "Mozilla/5.0 (X11; Linux i686; rv:112.0) Gecko/20100101
|
|||||||
|
|
||||||
|
|
||||||
class ExtensionManager(Gtk.Window):
|
class ExtensionManager(Gtk.Window):
|
||||||
ICON_INFO = "emblem-important-symbolic"
|
ICON_INFO = "emblem-synchronizing-symbolic"
|
||||||
ICON_UPDATE = "network-receive-symbolic"
|
ICON_UPDATE = "network-receive-symbolic"
|
||||||
|
|
||||||
class Column(IntEnum):
|
class Column(IntEnum):
|
||||||
@@ -193,6 +193,22 @@ class ExtensionManager(Gtk.Window):
|
|||||||
|
|
||||||
self.connect("delete-event", lambda w, e: self._app.app_settings.add(ws_property, w.get_size()))
|
self.connect("delete-event", lambda w, e: self._app.app_settings.add(ws_property, w.get_size()))
|
||||||
self.connect("realize", self.init)
|
self.connect("realize", self.init)
|
||||||
|
self.connect("show", self.on_show)
|
||||||
|
|
||||||
|
def on_show(self, window):
|
||||||
|
enabled = self._app.app_settings.extensions_support
|
||||||
|
self.set_sensitive(enabled)
|
||||||
|
if not enabled:
|
||||||
|
msg = f"\n{translate('Extension support is disabled!')}\n\n\t{translate('Do you want to enable it?')}"
|
||||||
|
if show_dialog(DialogType.QUESTION, self, msg) != Gtk.ResponseType.OK:
|
||||||
|
self.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
self._app.app_settings.extensions_support = True
|
||||||
|
self._app.show_info_message(translate('Restart the program to apply all changes.'), Gtk.MessageType.WARNING)
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def init(self, widget):
|
def init(self, widget):
|
||||||
self._load_spinner.start()
|
self._load_spinner.start()
|
||||||
@@ -281,6 +297,7 @@ class ExtensionManager(Gtk.Window):
|
|||||||
ext_ver = ext[0].VERSION
|
ext_ver = ext[0].VERSION
|
||||||
path = ext[1]
|
path = ext[1]
|
||||||
if ext_ver < ver:
|
if ext_ver < ver:
|
||||||
|
desc = f"[ Update -> ver. {ver} ] {desc}"
|
||||||
ver = ext_ver
|
ver = ext_ver
|
||||||
info = self.ICON_INFO
|
info = self.ICON_INFO
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -856,11 +856,10 @@ class FtpClientBox(Gtk.HBox):
|
|||||||
self._settings.ftp_bookmarks = [r[0] for r in self._bookmark_model]
|
self._settings.ftp_bookmarks = [r[0] for r in self._bookmark_model]
|
||||||
|
|
||||||
def on_view_key_press(self, view, event):
|
def on_view_key_press(self, view, event):
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
|
|
||||||
if key is KeyboardKey.F7:
|
if key is KeyboardKey.F7:
|
||||||
|
|||||||
@@ -426,10 +426,9 @@ class ImportDialog:
|
|||||||
|
|
||||||
def on_key_press(self, view, event):
|
def on_key_press(self, view, event):
|
||||||
""" Handling keystrokes """
|
""" Handling keystrokes """
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
|
|
||||||
if key is KeyboardKey.SPACE:
|
if key is KeyboardKey.SPACE:
|
||||||
model = view.get_model()
|
model = view.get_model()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -1331,10 +1331,9 @@ class YtListImportDialog:
|
|||||||
view.get_model().foreach(lambda mod, path, itr: mod.set_value(itr, 2, select))
|
view.get_model().foreach(lambda mod, path, itr: mod.set_value(itr, 2, select))
|
||||||
|
|
||||||
def on_key_press(self, view, event):
|
def on_key_press(self, view, event):
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
|
|
||||||
if key is KeyboardKey.SPACE:
|
if key is KeyboardKey.SPACE:
|
||||||
path, column = view.get_cursor()
|
path, column = view.get_cursor()
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
app/ui/lang/sk/LC_MESSAGES/demon-editor.mo
Normal file
BIN
app/ui/lang/sk/LC_MESSAGES/demon-editor.mo
Normal file
Binary file not shown.
Binary file not shown.
@@ -1852,7 +1852,7 @@ Author: Dmitriy Yefremov
|
|||||||
<object class="GtkLabel" id="app_ver_label">
|
<object class="GtkLabel" id="app_ver_label">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="label">3.14.0 Beta</property>
|
<property name="label">3.14.4 Beta</property>
|
||||||
<attributes>
|
<attributes>
|
||||||
<attribute name="weight" value="bold"/>
|
<attribute name="weight" value="bold"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -69,7 +69,7 @@ from .iptv import (IptvDialog, SearchUnavailableDialog, IptvListConfigurationDia
|
|||||||
from .main_helper import *
|
from .main_helper import *
|
||||||
from .picons import PiconManager
|
from .picons import PiconManager
|
||||||
from .search import SearchProvider
|
from .search import SearchProvider
|
||||||
from .service_details_dialog import ServiceDetailsDialog, Action
|
from .service_dialog import ServiceDetailsDialog, Action
|
||||||
from .settings_dialog import SettingsDialog
|
from .settings_dialog import SettingsDialog
|
||||||
from .uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column,
|
from .uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column,
|
||||||
MOD_MASK, APP_FONT, Page, HeaderBar, LINK_ICON)
|
MOD_MASK, APP_FONT, Page, HeaderBar, LINK_ICON)
|
||||||
@@ -79,7 +79,7 @@ from .xml.edit import SatellitesTool
|
|||||||
|
|
||||||
class Application(Gtk.Application):
|
class Application(Gtk.Application):
|
||||||
""" Main application class. """
|
""" Main application class. """
|
||||||
VERSION = "3.14.0"
|
VERSION = "3.14.4"
|
||||||
|
|
||||||
SERVICE_MODEL = "services_list_store"
|
SERVICE_MODEL = "services_list_store"
|
||||||
FAV_MODEL = "fav_list_store"
|
FAV_MODEL = "fav_list_store"
|
||||||
@@ -88,12 +88,13 @@ class Application(Gtk.Application):
|
|||||||
IPTV_MODEL = "iptv_list_store"
|
IPTV_MODEL = "iptv_list_store"
|
||||||
DRAG_SEP = "::::"
|
DRAG_SEP = "::::"
|
||||||
|
|
||||||
MARKER_TYPES = {BqServiceType.MARKER.name, BqServiceType.SPACE.name, BqServiceType.ALT.name}
|
MARKER_TYPES = {BqServiceType.MARKER.name, BqServiceType.SPACE.name}
|
||||||
|
NON_REF_TYPES = {BqServiceType.MARKER.name, BqServiceType.SPACE.name, BqServiceType.ALT.name}
|
||||||
|
|
||||||
DEL_FACTOR = 100 # Batch size to delete in one pass.
|
DEL_FACTOR = 100 # Batch size to delete in one pass.
|
||||||
FAV_FACTOR = DEL_FACTOR * 5
|
FAV_FACTOR = DEL_FACTOR * 5
|
||||||
|
|
||||||
_TV_TYPES = ("TV", "TV (HD)", "TV (UHD)", "TV (H264)")
|
_TV_TYPES = {"TV", "TV (HD)", "TV (UHD)", "TV (H264)"}
|
||||||
|
|
||||||
BG_TASK_LIMIT = 5
|
BG_TASK_LIMIT = 5
|
||||||
|
|
||||||
@@ -721,18 +722,21 @@ class Application(Gtk.Application):
|
|||||||
self.on_iptv_list_configuration, self.on_remove_all_unavailable):
|
self.on_iptv_list_configuration, self.on_remove_all_unavailable):
|
||||||
iptv_elem.bind_property("sensitive", self.set_action(h.__name__, h, False), "enabled")
|
iptv_elem.bind_property("sensitive", self.set_action(h.__name__, h, False), "enabled")
|
||||||
|
|
||||||
if self._settings.extensions_support:
|
self.init_extensions(builder)
|
||||||
self.init_extensions(builder)
|
|
||||||
|
|
||||||
def init_extensions(self, builder):
|
def init_extensions(self, builder):
|
||||||
import pkgutil
|
|
||||||
from importlib.util import module_from_spec
|
|
||||||
from app.ui.extensions.management import ExtensionManager
|
from app.ui.extensions.management import ExtensionManager
|
||||||
# Extensions (Plugins) section.
|
# Extensions (Plugins) section.
|
||||||
ext_section = builder.get_object(f"{'mac_' if IS_DARWIN else ''}extension_section")
|
ext_section = builder.get_object(f"{'mac_' if IS_DARWIN else ''}extension_section")
|
||||||
self.set_action("on_extension_manager", lambda a, v: ExtensionManager(self).show())
|
self.set_action("on_extension_manager", lambda a, v: ExtensionManager(self).show())
|
||||||
ext_section.append_item(Gio.MenuItem.new(translate("Extension Manager"), "app.on_extension_manager"))
|
ext_section.append_item(Gio.MenuItem.new(translate("Extension Manager"), "app.on_extension_manager"))
|
||||||
|
|
||||||
|
if not self._settings.extensions_support:
|
||||||
|
return
|
||||||
|
|
||||||
|
import pkgutil
|
||||||
|
from importlib.util import module_from_spec
|
||||||
|
|
||||||
ext_path = f"{self._settings.default_data_path}tools{os.sep}extensions"
|
ext_path = f"{self._settings.default_data_path}tools{os.sep}extensions"
|
||||||
ext_paths = [f"{os.path.dirname(__file__)}{os.sep}extensions", ext_path, "extensions"]
|
ext_paths = [f"{os.path.dirname(__file__)}{os.sep}extensions", ext_path, "extensions"]
|
||||||
extensions = {}
|
extensions = {}
|
||||||
@@ -1104,7 +1108,17 @@ class Application(Gtk.Application):
|
|||||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(self.quit)
|
if len(self._task_box):
|
||||||
|
msg = f"{translate('There are running background tasks!')}\n\n\t\t{translate('Are you sure?')}"
|
||||||
|
if show_dialog(DialogType.QUESTION, self._main_window, msg) != Gtk.ResponseType.OK:
|
||||||
|
return True
|
||||||
|
log("Terminating the application...")
|
||||||
|
import signal
|
||||||
|
|
||||||
|
os.kill(os.getpid(), signal.SIGTERM)
|
||||||
|
GLib.idle_add(self.quit, priority=GLib.PRIORITY_HIGH)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def on_main_window_state(self, window, event):
|
def on_main_window_state(self, window, event):
|
||||||
if event.new_window_state & Gdk.WindowState.FULLSCREEN or event.new_window_state & Gdk.WindowState.MAXIMIZED:
|
if event.new_window_state & Gdk.WindowState.FULLSCREEN or event.new_window_state & Gdk.WindowState.MAXIMIZED:
|
||||||
@@ -1214,7 +1228,7 @@ class Application(Gtk.Application):
|
|||||||
|
|
||||||
msg = translate("Restart the program to apply all changes.")
|
msg = translate("Restart the program to apply all changes.")
|
||||||
if value:
|
if value:
|
||||||
warn = "It can cause some problems."
|
warn = translate("It can cause some problems.")
|
||||||
msg = f"{translate('EXPERIMENTAL!')} {warn} {msg}"
|
msg = f"{translate('EXPERIMENTAL!')} {warn} {msg}"
|
||||||
self.show_info_message(msg, Gtk.MessageType.WARNING)
|
self.show_info_message(msg, Gtk.MessageType.WARNING)
|
||||||
|
|
||||||
@@ -1288,7 +1302,7 @@ class Application(Gtk.Application):
|
|||||||
def fav_service_data_func(self, column, renderer, model, itr, data):
|
def fav_service_data_func(self, column, renderer, model, itr, data):
|
||||||
if self._display_epg and self._s_type is SettingsType.ENIGMA_2:
|
if self._display_epg and self._s_type is SettingsType.ENIGMA_2:
|
||||||
srv_name = model.get_value(itr, Column.FAV_SERVICE)
|
srv_name = model.get_value(itr, Column.FAV_SERVICE)
|
||||||
if model.get_value(itr, Column.FAV_TYPE) in self.MARKER_TYPES:
|
if model.get_value(itr, Column.FAV_TYPE) in self.NON_REF_TYPES:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
event = self._epg_cache.get_current_event(srv_name)
|
event = self._epg_cache.get_current_event(srv_name)
|
||||||
@@ -1473,7 +1487,7 @@ class Application(Gtk.Application):
|
|||||||
priority = GLib.PRIORITY_LOW
|
priority = GLib.PRIORITY_LOW
|
||||||
|
|
||||||
if model_name == self.FAV_MODEL:
|
if model_name == self.FAV_MODEL:
|
||||||
gen = self.remove_favs(itrs, model)
|
gen = self.remove_favorites(itrs, model)
|
||||||
elif model_name == self.BQ_MODEL:
|
elif model_name == self.BQ_MODEL:
|
||||||
gen = self.delete_bouquets(itrs, model)
|
gen = self.delete_bouquets(itrs, model)
|
||||||
priority = GLib.PRIORITY_DEFAULT
|
priority = GLib.PRIORITY_DEFAULT
|
||||||
@@ -1489,18 +1503,24 @@ class Application(Gtk.Application):
|
|||||||
|
|
||||||
return rows
|
return rows
|
||||||
|
|
||||||
def remove_favs(self, itrs, model):
|
def remove_favorites(self, itrs, model):
|
||||||
""" Deleting bouquet services. """
|
""" Deleting bouquet services. """
|
||||||
if self._bq_selected:
|
if self._bq_selected:
|
||||||
fav_bouquet = self._bouquets.get(self._bq_selected, None)
|
fav_bouquet = self._bouquets.get(self._bq_selected, None)
|
||||||
if fav_bouquet:
|
if fav_bouquet:
|
||||||
|
removed = []
|
||||||
for index, itr in enumerate(itrs):
|
for index, itr in enumerate(itrs):
|
||||||
del fav_bouquet[int(model.get_path(itr)[0])]
|
path = model.get_path(itr)
|
||||||
|
p_index = int(path[0])
|
||||||
|
removed.append((p_index, tuple(model[path])))
|
||||||
|
del fav_bouquet[p_index]
|
||||||
self._fav_model.remove(itr)
|
self._fav_model.remove(itr)
|
||||||
|
|
||||||
if index % self.DEL_FACTOR == 0:
|
if index % self.DEL_FACTOR == 0:
|
||||||
yield True
|
yield True
|
||||||
|
|
||||||
self.update_fav_num_column(model)
|
self.update_fav_num_column(model)
|
||||||
self.emit("fav-removed", self._bq_selected)
|
self.emit("fav-removed", removed)
|
||||||
|
|
||||||
self.on_model_changed(self._fav_model)
|
self.on_model_changed(self._fav_model)
|
||||||
self._wait_dialog.hide()
|
self._wait_dialog.hide()
|
||||||
@@ -2130,11 +2150,15 @@ class Application(Gtk.Application):
|
|||||||
name, model = get_model_data(view)
|
name, model = get_model_data(view)
|
||||||
self.delete_views_selection(name)
|
self.delete_views_selection(name)
|
||||||
elif event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS and event.button == Gdk.BUTTON_PRIMARY:
|
elif event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS and event.button == Gdk.BUTTON_PRIMARY:
|
||||||
|
self._select_enabled = True
|
||||||
if self._settings.main_list_playback and self._fav_click_mode is not PlaybackMode.DISABLED:
|
if self._settings.main_list_playback and self._fav_click_mode is not PlaybackMode.DISABLED:
|
||||||
if view is self._services_view:
|
if view is self._services_view:
|
||||||
self.emit("srv-clicked", self._fav_click_mode)
|
self.emit("srv-clicked", self._fav_click_mode)
|
||||||
elif view is self._iptv_services_view:
|
elif view is self._iptv_services_view:
|
||||||
self.emit("iptv-clicked", self._fav_click_mode)
|
self.emit("iptv-clicked", self._fav_click_mode)
|
||||||
|
else:
|
||||||
|
if view is not self._fav_view:
|
||||||
|
self.on_edit()
|
||||||
|
|
||||||
def on_view_release(self, view, event):
|
def on_view_release(self, view, event):
|
||||||
""" Handles a mouse click (release) to view. """
|
""" Handles a mouse click (release) to view. """
|
||||||
@@ -2272,6 +2296,7 @@ class Application(Gtk.Application):
|
|||||||
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings, title="Open folder")
|
response = show_dialog(DialogType.CHOOSER, self._main_window, settings=self._settings, title="Open folder")
|
||||||
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
if response in (Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.open_data(response)
|
self.open_data(response)
|
||||||
|
|
||||||
def on_data_extract(self, app, page):
|
def on_data_extract(self, app, page):
|
||||||
@@ -2347,6 +2372,7 @@ class Application(Gtk.Application):
|
|||||||
self._alt_revealer.set_visible(False)
|
self._alt_revealer.set_visible(False)
|
||||||
self._filter_services_button.set_active(False)
|
self._filter_services_button.set_active(False)
|
||||||
self._wait_dialog.show()
|
self._wait_dialog.show()
|
||||||
|
self._services_progress_bar.show()
|
||||||
|
|
||||||
yield from self.clear_current_data()
|
yield from self.clear_current_data()
|
||||||
# Reset of sorting
|
# Reset of sorting
|
||||||
@@ -2405,6 +2431,7 @@ class Application(Gtk.Application):
|
|||||||
finally:
|
finally:
|
||||||
self._profile_combo_box.set_sensitive(True)
|
self._profile_combo_box.set_sensitive(True)
|
||||||
self._wait_dialog.hide()
|
self._wait_dialog.hide()
|
||||||
|
self._services_progress_bar.hide()
|
||||||
self.emit("data-load-done", self._settings.current_profile)
|
self.emit("data-load-done", self._settings.current_profile)
|
||||||
|
|
||||||
def append_data(self, bouquets, services):
|
def append_data(self, bouquets, services):
|
||||||
@@ -2536,7 +2563,6 @@ class Application(Gtk.Application):
|
|||||||
break
|
break
|
||||||
|
|
||||||
def append_services(self, services):
|
def append_services(self, services):
|
||||||
self._services_progress_bar.show()
|
|
||||||
to_add = []
|
to_add = []
|
||||||
for srv in services:
|
for srv in services:
|
||||||
if srv.fav_id not in self._services:
|
if srv.fav_id not in self._services:
|
||||||
@@ -2557,7 +2583,6 @@ class Application(Gtk.Application):
|
|||||||
self._services_progress_bar.set_fraction(index / size)
|
self._services_progress_bar.set_fraction(index / size)
|
||||||
yield True
|
yield True
|
||||||
|
|
||||||
self._services_progress_bar.hide()
|
|
||||||
yield True
|
yield True
|
||||||
|
|
||||||
def append_iptv_data(self, services=None):
|
def append_iptv_data(self, services=None):
|
||||||
@@ -2779,7 +2804,7 @@ class Application(Gtk.Application):
|
|||||||
self._alt_revealer.set_visible(False)
|
self._alt_revealer.set_visible(False)
|
||||||
self.on_info_bar_close()
|
self.on_info_bar_close()
|
||||||
|
|
||||||
if self._page is Page.EPG and srv.service_type not in self.MARKER_TYPES:
|
if self._page is Page.EPG and srv.service_type not in self.NON_REF_TYPES:
|
||||||
self.emit("fav-changed", srv)
|
self.emit("fav-changed", srv)
|
||||||
|
|
||||||
def on_services_selection(self, model, path, column):
|
def on_services_selection(self, model, path, column):
|
||||||
@@ -2963,11 +2988,10 @@ class Application(Gtk.Application):
|
|||||||
|
|
||||||
def on_tree_view_key_press(self, view, event):
|
def on_tree_view_key_press(self, view, event):
|
||||||
""" Handling keystrokes on press """
|
""" Handling keystrokes on press """
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return False
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
if key is KeyboardKey.F:
|
if key is KeyboardKey.F:
|
||||||
if ctrl:
|
if ctrl:
|
||||||
@@ -3016,11 +3040,10 @@ class Application(Gtk.Application):
|
|||||||
|
|
||||||
def on_tree_view_key_release(self, view, event):
|
def on_tree_view_key_release(self, view, event):
|
||||||
""" Handling keystrokes on release """
|
""" Handling keystrokes on release """
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
shift = event.state & Gdk.ModifierType.SHIFT_MASK
|
shift = event.state & Gdk.ModifierType.SHIFT_MASK
|
||||||
model_name, model = get_model_data(view)
|
model_name, model = get_model_data(view)
|
||||||
@@ -3158,9 +3181,9 @@ class Application(Gtk.Application):
|
|||||||
def on_fav_press(self, menu, event):
|
def on_fav_press(self, menu, event):
|
||||||
if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
if event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
||||||
if self._fav_click_mode is PlaybackMode.DISABLED:
|
if self._fav_click_mode is PlaybackMode.DISABLED:
|
||||||
return
|
self.on_service_edit(self._fav_view)
|
||||||
|
else:
|
||||||
self.emit("fav-clicked", self._fav_click_mode)
|
self.emit("fav-clicked", self._fav_click_mode)
|
||||||
else:
|
else:
|
||||||
return self.on_view_popup_menu(menu, event)
|
return self.on_view_popup_menu(menu, event)
|
||||||
|
|
||||||
@@ -3245,7 +3268,7 @@ class Application(Gtk.Application):
|
|||||||
fav_bqt = self._bouquets.get(self._bq_selected, None)
|
fav_bqt = self._bouquets.get(self._bq_selected, None)
|
||||||
response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, self._s_type).show()
|
response = SearchUnavailableDialog(self._main_window, self._fav_model, fav_bqt, iptv_rows, self._s_type).show()
|
||||||
if response:
|
if response:
|
||||||
gen = self.remove_favs(response, self._fav_model)
|
gen = self.remove_favorites(response, self._fav_model)
|
||||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||||
|
|
||||||
def on_reference_assign(self, view):
|
def on_reference_assign(self, view):
|
||||||
@@ -3676,7 +3699,7 @@ class Application(Gtk.Application):
|
|||||||
row = self._fav_model[path][:]
|
row = self._fav_model[path][:]
|
||||||
srv_type, fav_id = row[Column.FAV_TYPE], row[Column.FAV_ID]
|
srv_type, fav_id = row[Column.FAV_TYPE], row[Column.FAV_ID]
|
||||||
|
|
||||||
if srv_type in self.MARKER_TYPES and show_error:
|
if srv_type in self.NON_REF_TYPES and show_error:
|
||||||
self.show_error_message("Not allowed in this context!")
|
self.show_error_message("Not allowed in this context!")
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -4036,7 +4059,7 @@ class Application(Gtk.Application):
|
|||||||
if srv_type == BqServiceType.ALT.name:
|
if srv_type == BqServiceType.ALT.name:
|
||||||
return self.show_error_message("Operation not allowed in this context!")
|
return self.show_error_message("Operation not allowed in this context!")
|
||||||
|
|
||||||
if srv_type in self.MARKER_TYPES:
|
if srv_type in self.NON_REF_TYPES:
|
||||||
return self.on_rename(view)
|
return self.on_rename(view)
|
||||||
elif srv_type == BqServiceType.IPTV.name:
|
elif srv_type == BqServiceType.IPTV.name:
|
||||||
return self.on_iptv_service_edit(model[paths][Column.FAV_ID], view)
|
return self.on_iptv_service_edit(model[paths][Column.FAV_ID], view)
|
||||||
@@ -4166,7 +4189,7 @@ class Application(Gtk.Application):
|
|||||||
""" Marks services with duplicate [names] in the fav list. """
|
""" Marks services with duplicate [names] in the fav list. """
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
|
|
||||||
dup = Counter(r[Column.FAV_SERVICE] for r in self._fav_model if r[Column.FAV_TYPE] not in self.MARKER_TYPES)
|
dup = Counter(r[Column.FAV_SERVICE] for r in self._fav_model if r[Column.FAV_TYPE] not in self.NON_REF_TYPES)
|
||||||
dup = {k for k, v in dup.items() if v > 1}
|
dup = {k for k, v in dup.items() if v > 1}
|
||||||
|
|
||||||
for r in self._fav_model:
|
for r in self._fav_model:
|
||||||
@@ -4187,7 +4210,7 @@ class Application(Gtk.Application):
|
|||||||
if count:
|
if count:
|
||||||
if show_dialog(DialogType.QUESTION, self._main_window) != Gtk.ResponseType.OK:
|
if show_dialog(DialogType.QUESTION, self._main_window) != Gtk.ResponseType.OK:
|
||||||
return
|
return
|
||||||
gen = self.remove_favs(to_remove, self._fav_model)
|
gen = self.remove_favorites(to_remove, self._fav_model)
|
||||||
GLib.idle_add(lambda: next(gen, False))
|
GLib.idle_add(lambda: next(gen, False))
|
||||||
self.show_info_message(f"{translate('Done!')} {translate('Removed')}: {count}")
|
self.show_info_message(f"{translate('Done!')} {translate('Removed')}: {count}")
|
||||||
else:
|
else:
|
||||||
@@ -4527,11 +4550,10 @@ class Application(Gtk.Application):
|
|||||||
self.emit("fav-changed", srv)
|
self.emit("fav-changed", srv)
|
||||||
|
|
||||||
def on_alt_view_key_press(self, view, event):
|
def on_alt_view_key_press(self, view, event):
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
|
|
||||||
if ctrl and key == KeyboardKey.V:
|
if ctrl and key == KeyboardKey.V:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2024 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -976,11 +976,10 @@ class PiconManager(Gtk.Box):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def on_tree_view_key_press(self, view, event):
|
def on_tree_view_key_press(self, view, event):
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
if key is KeyboardKey.DELETE:
|
if key is KeyboardKey.DELETE:
|
||||||
self.on_local_remove(view)
|
self.on_local_remove(view)
|
||||||
|
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ class PlayerBox(Gtk.Overlay):
|
|||||||
width, height = size
|
width, height = size
|
||||||
|
|
||||||
if self._playback_window:
|
if self._playback_window:
|
||||||
self._playback_window.show()
|
self._playback_window.present()
|
||||||
self._playback_window.set_title(title or self.get_playback_title())
|
self._playback_window.set_title(title or self.get_playback_title())
|
||||||
else:
|
else:
|
||||||
self._playback_window = Gtk.Window(title=title or self.get_playback_title(),
|
self._playback_window = Gtk.Window(title=title or self.get_playback_title(),
|
||||||
|
|||||||
@@ -304,11 +304,10 @@ class RecordingsTool(Gtk.Box):
|
|||||||
self._filter_entry.grab_focus() if button.get_active() else self._filter_entry.set_text("")
|
self._filter_entry.grab_focus() if button.get_active() else self._filter_entry.set_text("")
|
||||||
|
|
||||||
def on_recordings_key_press(self, view, event):
|
def on_recordings_key_press(self, view, event):
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
if key is KeyboardKey.DELETE:
|
if key is KeyboardKey.DELETE:
|
||||||
self.on_recording_remove()
|
self.on_recording_remove()
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2018-2024 Dmitriy Yefremov
|
Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -32,7 +32,7 @@ Author: Dmitriy Yefremov
|
|||||||
<!-- interface-license-type mit -->
|
<!-- interface-license-type mit -->
|
||||||
<!-- interface-name DemonEditor -->
|
<!-- interface-name DemonEditor -->
|
||||||
<!-- interface-description Enigma2 channel and satellite list editor for GNU/Linux. -->
|
<!-- interface-description Enigma2 channel and satellite list editor for GNU/Linux. -->
|
||||||
<!-- interface-copyright 2018-2024 Dmitriy Yefremov -->
|
<!-- interface-copyright 2018-2026 Dmitriy Yefremov -->
|
||||||
<!-- interface-authors Dmitriy Yefremov -->
|
<!-- interface-authors Dmitriy Yefremov -->
|
||||||
<object class="GtkListStore" id="fec_list_store">
|
<object class="GtkListStore" id="fec_list_store">
|
||||||
<columns>
|
<columns>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -41,7 +41,7 @@ from .dialogs import show_dialog, DialogType, Action, get_builder
|
|||||||
from .main_helper import get_base_model, scroll_to
|
from .main_helper import get_base_model, scroll_to
|
||||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, CODED_ICON, Column
|
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HIDE_ICON, CODED_ICON, Column
|
||||||
|
|
||||||
_UI_PATH = UI_RESOURCES_PATH + "service_details_dialog.glade"
|
_UI_PATH = f"{UI_RESOURCES_PATH}service_dialog.glade"
|
||||||
|
|
||||||
|
|
||||||
class ServiceDetailsDialog:
|
class ServiceDetailsDialog:
|
||||||
@@ -92,15 +92,15 @@ class ServiceDetailsDialog:
|
|||||||
self._transponder_services_iters = None
|
self._transponder_services_iters = None
|
||||||
self._current_model = None
|
self._current_model = None
|
||||||
self._current_itr = None
|
self._current_itr = None
|
||||||
# Patterns
|
# Patterns.
|
||||||
self._DIGIT_PATTERN = re.compile("\\D")
|
self._DIGIT_PATTERN = re.compile("\\D")
|
||||||
self._NON_EMPTY_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
self._NON_EMPTY_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
||||||
self._CAID_PATTERN = re.compile("(?:^[\\s]*$)|(C:[0-9a-fA-F]{1,4})(,C:[0-9a-fA-F]{1,4})*")
|
self._CAID_PATTERN = re.compile("(?:^[\\s]*$)|(C:[0-9a-fA-F]{1,4})(,C:[0-9a-fA-F]{1,4})*")
|
||||||
self._PIDS_PATTERN = re.compile("(?:^[\\s]*$)|(c:[0-9]{2}[0-9a-fA-F]{1,4})(,c:[0-9]{2}[0-9a-fA-F]{1,4})*?")
|
self._PIDS_PATTERN = re.compile("(?:^[\\s]*$)|(c:[0-9]{2}[0-9a-fA-F]{1,4})(,c:[0-9]{2}[0-9a-fA-F]{1,4})*?")
|
||||||
# Buttons
|
# Buttons.
|
||||||
self._apply_button = builder.get_object("apply_button")
|
self._apply_button = builder.get_object("apply_button")
|
||||||
self._create_button = builder.get_object("create_button")
|
self._create_button = builder.get_object("create_button")
|
||||||
# style
|
# Style.
|
||||||
self._style_provider = Gtk.CssProvider()
|
self._style_provider = Gtk.CssProvider()
|
||||||
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
self._style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||||
# initialization only digit elements
|
# initialization only digit elements
|
||||||
@@ -352,7 +352,7 @@ class ServiceDetailsDialog:
|
|||||||
self.select_active_text(self._mod_combo_box, MODULATION.get(tr_data[8]))
|
self.select_active_text(self._mod_combo_box, MODULATION.get(tr_data[8]))
|
||||||
self.select_active_text(self._rolloff_combo_box, ROLL_OFF.get(tr_data[9]))
|
self.select_active_text(self._rolloff_combo_box, ROLL_OFF.get(tr_data[9]))
|
||||||
self.select_active_text(self._pilot_combo_box, Pilot(tr_data[10]).name)
|
self.select_active_text(self._pilot_combo_box, Pilot(tr_data[10]).name)
|
||||||
self._tr_flag_entry.set_text(tr_data[7])
|
self._tr_flag_entry.set_text(tr_data[6])
|
||||||
if data_len > 12:
|
if data_len > 12:
|
||||||
self._stream_id_entry.set_text(tr_data[11])
|
self._stream_id_entry.set_text(tr_data[11])
|
||||||
self._pls_code_entry.set_text(tr_data[12])
|
self._pls_code_entry.set_text(tr_data[12])
|
||||||
@@ -411,6 +411,12 @@ class ServiceDetailsDialog:
|
|||||||
""" Sat positions initialisation """
|
""" Sat positions initialisation """
|
||||||
self._sat_pos_button.set_value(float(sat_pos[:-1]))
|
self._sat_pos_button.set_value(float(sat_pos[:-1]))
|
||||||
self._pos_side_box.set_active_id(sat_pos[-1:])
|
self._pos_side_box.set_active_id(sat_pos[-1:])
|
||||||
|
self._sat_pos_button.connect("value-changed", self.on_sat_value_changed)
|
||||||
|
|
||||||
|
def on_sat_value_changed(self, button):
|
||||||
|
pos = int(self.get_sat_position())
|
||||||
|
namespace = int(f"{3600 - abs(pos) if pos < 0 else pos:04x}0000", 16)
|
||||||
|
self._namespace_entry.set_text(str(namespace))
|
||||||
|
|
||||||
def on_system_changed(self, box):
|
def on_system_changed(self, box):
|
||||||
if not self._tr_edit_switch.get_active():
|
if not self._tr_edit_switch.get_active():
|
||||||
@@ -463,7 +469,7 @@ class ServiceDetailsDialog:
|
|||||||
service, data = srv_data
|
service, data = srv_data
|
||||||
itr = self._current_model.append(service + (None, data.get(Column.SRV_BACKGROUND, None)))
|
itr = self._current_model.append(service + (None, data.get(Column.SRV_BACKGROUND, None)))
|
||||||
scroll_to(self._current_model.get_path(itr), self._services_view)
|
scroll_to(self._current_model.get_path(itr), self._services_view)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_edit(self):
|
def on_edit(self):
|
||||||
@@ -678,14 +684,13 @@ class ServiceDetailsDialog:
|
|||||||
sat_pos = self.get_sat_position()
|
sat_pos = self.get_sat_position()
|
||||||
|
|
||||||
inv = get_value_by_name(Inversion, self._invertion_combo_box.get_active_id())
|
inv = get_value_by_name(Inversion, self._invertion_combo_box.get_active_id())
|
||||||
srv_sys = "0" # !!!
|
flag = self._tr_flag_entry.get_text() or "0"
|
||||||
|
|
||||||
if self._s_type is SettingsType.ENIGMA_2:
|
if self._s_type is SettingsType.ENIGMA_2:
|
||||||
dvb_s_tr = self._ENIGMA2_TRANSPONDER_DATA.format("s", freq, rate, pol, fec, sat_pos, inv, srv_sys)
|
dvb_s_tr = self._ENIGMA2_TRANSPONDER_DATA.format("s", freq, rate, pol, fec, sat_pos, inv, flag)
|
||||||
if sys == "DVB-S":
|
if sys == "DVB-S":
|
||||||
return dvb_s_tr
|
return dvb_s_tr
|
||||||
if sys == "DVB-S2":
|
if sys == "DVB-S2":
|
||||||
flag = self._tr_flag_entry.get_text()
|
|
||||||
mod = self.get_value_from_combobox_id(self._mod_combo_box, MODULATION)
|
mod = self.get_value_from_combobox_id(self._mod_combo_box, MODULATION)
|
||||||
roll_off = self.get_value_from_combobox_id(self._rolloff_combo_box, ROLL_OFF)
|
roll_off = self.get_value_from_combobox_id(self._rolloff_combo_box, ROLL_OFF)
|
||||||
pilot = get_value_by_name(Pilot, self._pilot_combo_box.get_active_id())
|
pilot = get_value_by_name(Pilot, self._pilot_combo_box.get_active_id())
|
||||||
@@ -694,7 +699,7 @@ class ServiceDetailsDialog:
|
|||||||
st_id = self._stream_id_entry.get_text()
|
st_id = self._stream_id_entry.get_text()
|
||||||
pls = f":{st_id}:{pls_code}:{pls_mode}" if pls_mode and pls_code and st_id else ""
|
pls = f":{st_id}:{pls_code}:{pls_mode}" if pls_mode and pls_code and st_id else ""
|
||||||
|
|
||||||
return f"{dvb_s_tr}:{flag}:{mod}:{roll_off}:{pilot}{pls}"
|
return f"{dvb_s_tr}:1:{mod}:{roll_off}:{pilot}{pls}"
|
||||||
|
|
||||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||||
tr_data = get_attributes(self._old_service.transponder)
|
tr_data = get_attributes(self._old_service.transponder)
|
||||||
@@ -2418,6 +2418,7 @@ Author: Dmitriy Yefremov
|
|||||||
<item id="nl_NL">Nederlands</item>
|
<item id="nl_NL">Nederlands</item>
|
||||||
<item id="pl_PL">Polski</item>
|
<item id="pl_PL">Polski</item>
|
||||||
<item id="pt_PT">Português</item>
|
<item id="pt_PT">Português</item>
|
||||||
|
<item id="sk_SK">Slovák</item>
|
||||||
<item id="tr_TR">Türkçe</item>
|
<item id="tr_TR">Türkçe</item>
|
||||||
<item id="be_BY">Беларуская</item>
|
<item id="be_BY">Беларуская</item>
|
||||||
<item id="ru_RU">Русский</item>
|
<item id="ru_RU">Русский</item>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -692,7 +692,7 @@ class SettingsDialog:
|
|||||||
if mode is PlaybackMode.PLAY:
|
if mode is PlaybackMode.PLAY:
|
||||||
self.show_info_message("Operates in standby mode or current active transponder!", Gtk.MessageType.WARNING)
|
self.show_info_message("Operates in standby mode or current active transponder!", Gtk.MessageType.WARNING)
|
||||||
elif mode is PlaybackMode.STREAM:
|
elif mode is PlaybackMode.STREAM:
|
||||||
self.show_info_message("Playback IPTV streams only!", Gtk.MessageType.WARNING)
|
self.show_info_message("Playback of IPTV streams only!", Gtk.MessageType.WARNING)
|
||||||
elif mode is PlaybackMode.DISABLED:
|
elif mode is PlaybackMode.DISABLED:
|
||||||
self._allow_main_list_playback_switch.set_active(False)
|
self._allow_main_list_playback_switch.set_active(False)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ paned.vertical > separator {
|
|||||||
background-size: 24px 2px;
|
background-size: 24px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
progressbar > trough {
|
||||||
|
min-width: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
.red-button {
|
.red-button {
|
||||||
background-image: none;
|
background-image: none;
|
||||||
background-color: red;
|
background-color: red;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -24,6 +24,8 @@
|
|||||||
#
|
#
|
||||||
# Author: Dmitriy Yefremov
|
# Author: Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from app.ui.dialogs import translate
|
from app.ui.dialogs import translate
|
||||||
from .uicommons import Gtk, GLib
|
from .uicommons import Gtk, GLib
|
||||||
|
|
||||||
@@ -52,14 +54,13 @@ class BGTaskWidget(Gtk.Box):
|
|||||||
self.pack_start(close_button, False, False, 0)
|
self.pack_start(close_button, False, False, 0)
|
||||||
|
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
# Just prototype. -> It may not work properly!
|
# Just prototype. -> It may not work properly!
|
||||||
# TODO: Different options need to be tested. Possibly with normal threads.
|
from gi.repository.Gio import Task, Cancellable
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
|
|
||||||
self._executor = ThreadPoolExecutor(max_workers=self.TASK_LIMIT)
|
self._task = Task.new(self, Cancellable.new(), lambda s, t: GLib.idle_add(self._app.emit, "task-done", self))
|
||||||
future = self._executor.submit(target, *args)
|
self._task.set_priority(GLib.PRIORITY_LOW)
|
||||||
future.add_done_callback(lambda f: GLib.idle_add(self._app.emit, "task-done", self))
|
self._task.set_return_on_cancel(True)
|
||||||
|
self._task.run_in_thread(lambda t, s, d, c: target(*args))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
@@ -78,7 +79,10 @@ class BGTaskWidget(Gtk.Box):
|
|||||||
self.set_tooltip_text(value)
|
self.set_tooltip_text(value)
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
self._executor.shutdown(wait=False)
|
cancelable = self._task.get_cancellable()
|
||||||
|
if cancelable:
|
||||||
|
cancelable.cancel()
|
||||||
|
|
||||||
self._app.emit("task-canceled", None)
|
self._app.emit("task-canceled", None)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -129,11 +129,10 @@ class TelnetClient(Gtk.Box):
|
|||||||
self.do_command()
|
self.do_command()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return None
|
return False
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
if ctrl and key is KeyboardKey.C:
|
if ctrl and key is KeyboardKey.C:
|
||||||
if self._tn and self._tn.sock:
|
if self._tn and self._tn.sock:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2024 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -460,11 +460,10 @@ class TimerTool(Gtk.Box):
|
|||||||
on_popup_menu(menu, event)
|
on_popup_menu(menu, event)
|
||||||
|
|
||||||
def on_timers_key_release(self, view, event):
|
def on_timers_key_release(self, view, event):
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
|
|
||||||
if key is KeyboardKey.DELETE:
|
if key is KeyboardKey.DELETE:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -290,14 +290,8 @@ class Column(IntEnum):
|
|||||||
|
|
||||||
# *************** Keyboard keys *************** #
|
# *************** Keyboard keys *************** #
|
||||||
|
|
||||||
class BaseKeyboardKey(Enum):
|
|
||||||
@classmethod
|
|
||||||
def value_exist(cls, value):
|
|
||||||
return value in (val.value for val in cls.__members__.values())
|
|
||||||
|
|
||||||
|
|
||||||
if IS_LINUX:
|
if IS_LINUX:
|
||||||
class KeyboardKey(BaseKeyboardKey):
|
class KeyboardKey(IntEnum):
|
||||||
""" The raw(hardware) codes [Linux] of the keyboard keys. """
|
""" The raw(hardware) codes [Linux] of the keyboard keys. """
|
||||||
E = 26
|
E = 26
|
||||||
R = 27
|
R = 27
|
||||||
@@ -335,8 +329,14 @@ if IS_LINUX:
|
|||||||
PAGE_UP_KP = 81
|
PAGE_UP_KP = 81
|
||||||
PAGE_DOWN_KP = 89
|
PAGE_DOWN_KP = 89
|
||||||
|
|
||||||
|
UNDEFINED = -1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _missing_(cls, value):
|
||||||
|
return cls.UNDEFINED
|
||||||
|
|
||||||
elif IS_DARWIN:
|
elif IS_DARWIN:
|
||||||
class KeyboardKey(BaseKeyboardKey):
|
class KeyboardKey(IntEnum):
|
||||||
""" The raw(hardware) codes [macOS] of the keyboard keys. """
|
""" The raw(hardware) codes [macOS] of the keyboard keys. """
|
||||||
F = 3
|
F = 3
|
||||||
E = 14
|
E = 14
|
||||||
@@ -376,8 +376,14 @@ elif IS_DARWIN:
|
|||||||
PAGE_UP_KP = -1
|
PAGE_UP_KP = -1
|
||||||
PAGE_DOWN_KP = -1
|
PAGE_DOWN_KP = -1
|
||||||
|
|
||||||
|
UNDEFINED = -1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _missing_(cls, value):
|
||||||
|
return cls.UNDEFINED
|
||||||
|
|
||||||
else:
|
else:
|
||||||
class KeyboardKey(BaseKeyboardKey):
|
class KeyboardKey(IntEnum):
|
||||||
""" The raw(hardware) codes [Windows] of the keyboard keys. """
|
""" The raw(hardware) codes [Windows] of the keyboard keys. """
|
||||||
E = 69
|
E = 69
|
||||||
R = 82
|
R = 82
|
||||||
@@ -415,6 +421,12 @@ else:
|
|||||||
PAGE_UP_KP = -1
|
PAGE_UP_KP = -1
|
||||||
PAGE_DOWN_KP = -1
|
PAGE_DOWN_KP = -1
|
||||||
|
|
||||||
|
UNDEFINED = -1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _missing_(cls, value):
|
||||||
|
return cls.UNDEFINED
|
||||||
|
|
||||||
# Keys for move in lists. KEY_KP_(NAME) for laptop!
|
# Keys for move in lists. KEY_KP_(NAME) for laptop!
|
||||||
MOVE_KEYS = {KeyboardKey.UP, KeyboardKey.PAGE_UP,
|
MOVE_KEYS = {KeyboardKey.UP, KeyboardKey.PAGE_UP,
|
||||||
KeyboardKey.DOWN, KeyboardKey.PAGE_DOWN,
|
KeyboardKey.DOWN, KeyboardKey.PAGE_DOWN,
|
||||||
@@ -422,5 +434,27 @@ MOVE_KEYS = {KeyboardKey.UP, KeyboardKey.PAGE_UP,
|
|||||||
KeyboardKey.HOME_KP, KeyboardKey.END_KP,
|
KeyboardKey.HOME_KP, KeyboardKey.END_KP,
|
||||||
KeyboardKey.PAGE_UP_KP, KeyboardKey.PAGE_DOWN_KP}
|
KeyboardKey.PAGE_UP_KP, KeyboardKey.PAGE_DOWN_KP}
|
||||||
|
|
||||||
|
|
||||||
|
class LoadingProgressBar(Gtk.ProgressBar):
|
||||||
|
""" A custom class for a progress bar.
|
||||||
|
|
||||||
|
Used as an alternative to Gtk.Spinner to reduce CPU load.
|
||||||
|
"""
|
||||||
|
__gtype_name__ = "LoadingProgressBar"
|
||||||
|
|
||||||
|
def __init__(self, **properties):
|
||||||
|
super().__init__(**properties)
|
||||||
|
|
||||||
|
self.connect("notify::visible", self.on_visible)
|
||||||
|
|
||||||
|
def on_visible(self, bar, param):
|
||||||
|
if self.get_visible():
|
||||||
|
GLib.timeout_add(500, self.update, priority=GLib.PRIORITY_LOW)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.pulse()
|
||||||
|
return self.get_visible()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.38.2
|
<!-- Generated with glade 3.40.0
|
||||||
|
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2018-2024 Dmitriy Yefremov
|
Copyright (c) 2018-2025 Dmitriy Yefremov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -31,7 +31,7 @@ Author: Dmitriy Yefremov
|
|||||||
<!-- interface-css-provider-path style.css -->
|
<!-- interface-css-provider-path style.css -->
|
||||||
<!-- interface-license-type mit -->
|
<!-- interface-license-type mit -->
|
||||||
<!-- interface-name DemonEditor -->
|
<!-- interface-name DemonEditor -->
|
||||||
<!-- interface-copyright 2018-2024 Dmitriy Yefremov -->
|
<!-- interface-copyright 2018-2025 Dmitriy Yefremov -->
|
||||||
<!-- interface-authors Dmitriy Yefremov -->
|
<!-- interface-authors Dmitriy Yefremov -->
|
||||||
<!-- n-columns=2 n-rows=4 -->
|
<!-- n-columns=2 n-rows=4 -->
|
||||||
<object class="GtkGrid" id="cable_tr_box">
|
<object class="GtkGrid" id="cable_tr_box">
|
||||||
@@ -879,7 +879,7 @@ Author: Dmitriy Yefremov
|
|||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=4 -->
|
<!-- n-columns=2 n-rows=5 -->
|
||||||
<object class="GtkGrid" id="tr_dialog_grid2">
|
<object class="GtkGrid" id="tr_dialog_grid2">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
@@ -911,7 +911,7 @@ Author: Dmitriy Yefremov
|
|||||||
<property name="max-width-chars">12</property>
|
<property name="max-width-chars">12</property>
|
||||||
<property name="primary-icon-name">document-edit-symbolic</property>
|
<property name="primary-icon-name">document-edit-symbolic</property>
|
||||||
<property name="primary-icon-activatable">False</property>
|
<property name="primary-icon-activatable">False</property>
|
||||||
<property name="placeholder-text" translatable="yes">0 - 262142</property>
|
<property name="placeholder-text">0 - 262142</property>
|
||||||
<property name="input-purpose">digits</property>
|
<property name="input-purpose">digits</property>
|
||||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
@@ -928,7 +928,7 @@ Author: Dmitriy Yefremov
|
|||||||
<property name="max-width-chars">12</property>
|
<property name="max-width-chars">12</property>
|
||||||
<property name="primary-icon-name">document-edit-symbolic</property>
|
<property name="primary-icon-name">document-edit-symbolic</property>
|
||||||
<property name="primary-icon-activatable">False</property>
|
<property name="primary-icon-activatable">False</property>
|
||||||
<property name="placeholder-text" translatable="yes">0 - 255</property>
|
<property name="placeholder-text">0 - 255</property>
|
||||||
<property name="input-purpose">digits</property>
|
<property name="input-purpose">digits</property>
|
||||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
@@ -945,7 +945,7 @@ Author: Dmitriy Yefremov
|
|||||||
<property name="max-width-chars">12</property>
|
<property name="max-width-chars">12</property>
|
||||||
<property name="primary-icon-name">document-edit-symbolic</property>
|
<property name="primary-icon-name">document-edit-symbolic</property>
|
||||||
<property name="primary-icon-activatable">False</property>
|
<property name="primary-icon-activatable">False</property>
|
||||||
<property name="placeholder-text" translatable="yes">0 - 255</property>
|
<property name="placeholder-text">0 - 255</property>
|
||||||
<property name="input-purpose">digits</property>
|
<property name="input-purpose">digits</property>
|
||||||
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
@@ -1095,6 +1095,58 @@ Author: Dmitriy Yefremov
|
|||||||
<property name="top-attach">3</property>
|
<property name="top-attach">3</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="tr_t2mi_pid_label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">T2-MI PID</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="label">:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">0</property>
|
||||||
|
<property name="top-attach">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEntry" id="t2mi_pid_entry">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="width-chars">5</property>
|
||||||
|
<property name="max-width-chars">12</property>
|
||||||
|
<property name="primary-icon-name">document-edit-symbolic</property>
|
||||||
|
<property name="primary-icon-activatable">False</property>
|
||||||
|
<property name="placeholder-text">0 - 8191</property>
|
||||||
|
<property name="input-purpose">digits</property>
|
||||||
|
<signal name="changed" handler="on_entry_changed" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">1</property>
|
||||||
|
<property name="top-attach">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">True</property>
|
<property name="expand">True</property>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2024 Dmitriy Yefremov
|
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -207,6 +207,7 @@ class SatTransponderDialog(TransponderDialog):
|
|||||||
self._pls_code_entry = builder.get_object("pls_code_entry")
|
self._pls_code_entry = builder.get_object("pls_code_entry")
|
||||||
self._is_id_entry = builder.get_object("is_id_entry")
|
self._is_id_entry = builder.get_object("is_id_entry")
|
||||||
self._t2mi_plp_id_entry = builder.get_object("t2mi_plp_id_entry")
|
self._t2mi_plp_id_entry = builder.get_object("t2mi_plp_id_entry")
|
||||||
|
self._t2mi_pid_entry = builder.get_object("t2mi_pid_entry")
|
||||||
|
|
||||||
self.set_style_provider(self._freq_entry)
|
self.set_style_provider(self._freq_entry)
|
||||||
self.set_style_provider(self._rate_entry)
|
self.set_style_provider(self._rate_entry)
|
||||||
@@ -230,6 +231,7 @@ class SatTransponderDialog(TransponderDialog):
|
|||||||
self._is_id_entry.set_text(transponder.is_id if transponder.is_id else "")
|
self._is_id_entry.set_text(transponder.is_id if transponder.is_id else "")
|
||||||
self._pls_code_entry.set_text(transponder.pls_code if transponder.pls_code else "")
|
self._pls_code_entry.set_text(transponder.pls_code if transponder.pls_code else "")
|
||||||
self._t2mi_plp_id_entry.set_text(transponder.t2mi_plp_id if transponder.t2mi_plp_id else "")
|
self._t2mi_plp_id_entry.set_text(transponder.t2mi_plp_id if transponder.t2mi_plp_id else "")
|
||||||
|
self._t2mi_pid_entry.set_text(transponder.t2mi_pid if transponder.t2mi_pid else "")
|
||||||
|
|
||||||
def to_transponder(self):
|
def to_transponder(self):
|
||||||
return Transponder(frequency=self._freq_entry.get_text(),
|
return Transponder(frequency=self._freq_entry.get_text(),
|
||||||
@@ -241,7 +243,8 @@ class SatTransponderDialog(TransponderDialog):
|
|||||||
pls_mode=get_key_by_value(PLS_MODE, self._pls_mode_box.get_active_id()),
|
pls_mode=get_key_by_value(PLS_MODE, self._pls_mode_box.get_active_id()),
|
||||||
pls_code=self._pls_code_entry.get_text(),
|
pls_code=self._pls_code_entry.get_text(),
|
||||||
is_id=self._is_id_entry.get_text(),
|
is_id=self._is_id_entry.get_text(),
|
||||||
t2mi_plp_id=self._t2mi_plp_id_entry.get_text())
|
t2mi_plp_id=self._t2mi_plp_id_entry.get_text(),
|
||||||
|
t2mi_pid=self._t2mi_pid_entry.get_text())
|
||||||
|
|
||||||
def is_accept(self):
|
def is_accept(self):
|
||||||
tr = self.to_transponder()
|
tr = self.to_transponder()
|
||||||
@@ -255,6 +258,8 @@ class SatTransponderDialog(TransponderDialog):
|
|||||||
return False
|
return False
|
||||||
elif self.digit_pattern.search(tr.t2mi_plp_id):
|
elif self.digit_pattern.search(tr.t2mi_plp_id):
|
||||||
return False
|
return False
|
||||||
|
elif self.digit_pattern.search(tr.t2mi_pid):
|
||||||
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#
|
#
|
||||||
# The MIT License (MIT)
|
# The MIT License (MIT)
|
||||||
#
|
#
|
||||||
# Copyright (c) 2018-2025 Dmitriy Yefremov
|
# Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
#
|
#
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -239,12 +239,6 @@ class SatellitesTool(Gtk.Box):
|
|||||||
self._transponders_stack.set_visible_child_name(self._dvb_type)
|
self._transponders_stack.set_visible_child_name(self._dvb_type)
|
||||||
self._update_header_button.set_sensitive(self._dvb_type is self.DVB.SAT)
|
self._update_header_button.set_sensitive(self._dvb_type is self.DVB.SAT)
|
||||||
|
|
||||||
if self._dvb_type is self.DVB.SAT:
|
|
||||||
self._app.on_info_bar_close()
|
|
||||||
|
|
||||||
else:
|
|
||||||
self._app.show_info_message("EXPERIMENTAL!", Gtk.MessageType.WARNING)
|
|
||||||
|
|
||||||
def on_satellite_selection(self, view):
|
def on_satellite_selection(self, view):
|
||||||
model = self._sat_tr_view.get_model()
|
model = self._sat_tr_view.get_model()
|
||||||
model.clear()
|
model.clear()
|
||||||
@@ -310,11 +304,10 @@ class SatellitesTool(Gtk.Box):
|
|||||||
|
|
||||||
def on_key_press(self, view, event):
|
def on_key_press(self, view, event):
|
||||||
""" Handling keystrokes. """
|
""" Handling keystrokes. """
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
|
|
||||||
if key is KeyboardKey.DELETE:
|
if key is KeyboardKey.DELETE:
|
||||||
@@ -329,12 +322,11 @@ class SatellitesTool(Gtk.Box):
|
|||||||
view.do_unselect_all(view)
|
view.do_unselect_all(view)
|
||||||
|
|
||||||
def on_tr_key_press(self, view, event):
|
def on_tr_key_press(self, view, event):
|
||||||
""" Handling transponder view keystrokes. """
|
""" Handling transponder view keystrokes. """
|
||||||
key_code = event.hardware_keycode
|
key = KeyboardKey(event.hardware_keycode)
|
||||||
if not KeyboardKey.value_exist(key_code):
|
if key is KeyboardKey.UNDEFINED:
|
||||||
return
|
return
|
||||||
|
|
||||||
key = KeyboardKey(key_code)
|
|
||||||
ctrl = event.state & MOD_MASK
|
ctrl = event.state & MOD_MASK
|
||||||
|
|
||||||
if key is KeyboardKey.DELETE:
|
if key is KeyboardKey.DELETE:
|
||||||
|
|||||||
@@ -128,6 +128,8 @@ Author: Dmitriy Yefremov
|
|||||||
<column type="gchararray"/>
|
<column type="gchararray"/>
|
||||||
<!-- column-name t2mi_plp_id -->
|
<!-- column-name t2mi_plp_id -->
|
||||||
<column type="gchararray"/>
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name t2mi_pid -->
|
||||||
|
<column type="gchararray"/>
|
||||||
</columns>
|
</columns>
|
||||||
<signal name="row-deleted" handler="on_sat_tr_model_changed" swapped="no"/>
|
<signal name="row-deleted" handler="on_sat_tr_model_changed" swapped="no"/>
|
||||||
<signal name="row-inserted" handler="on_sat_tr_model_changed" swapped="no"/>
|
<signal name="row-inserted" handler="on_sat_tr_model_changed" swapped="no"/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
VER="3.14.0_Beta"
|
VER="3.14.4_Beta"
|
||||||
B_PATH="dist/DemonEditor"
|
B_PATH="dist/DemonEditor"
|
||||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
Package: demon-editor
|
Package: demon-editor
|
||||||
Version: 3.14.0-Beta
|
Version: 3.14.4-Beta
|
||||||
Section: utils
|
Section: utils
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: all
|
Architecture: all
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ Source: https://github.com/DYefremov/DemonEditor
|
|||||||
Files: *
|
Files: *
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2018-2025 Dmitriy Yefremov
|
Copyright (c) 2018-2026 Dmitriy Yefremov
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ a = Analysis([EXE_NAME],
|
|||||||
pathex=PATH_EXE,
|
pathex=PATH_EXE,
|
||||||
binaries=None,
|
binaries=None,
|
||||||
datas=ui_files,
|
datas=ui_files,
|
||||||
hiddenimports=['fileinput', 'uuid', 'asyncio'],
|
hiddenimports=['fileinput', 'uuid', 'asyncio', 'getpass'],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
hooksconfig={
|
hooksconfig={
|
||||||
"gi": {
|
"gi": {
|
||||||
"languages": ["en", "be", "es", "it", "nl",
|
"languages": ["en", "be", "es", "it", "nl", "pl",
|
||||||
"pl", "pt", "ru", "tr", "zh_CN"],
|
"pt", "ru", "sk", "tr", "zh_CN"],
|
||||||
"module-versions": {
|
"module-versions": {
|
||||||
"Gtk": "3.0"
|
"Gtk": "3.0"
|
||||||
},
|
},
|
||||||
@@ -81,8 +81,8 @@ app = BUNDLE(coll,
|
|||||||
'CFBundleGetInfoString': "Enigma2 channel and satellite editor",
|
'CFBundleGetInfoString': "Enigma2 channel and satellite editor",
|
||||||
'LSApplicationCategoryType': 'public.app-category.utilities',
|
'LSApplicationCategoryType': 'public.app-category.utilities',
|
||||||
'LSMinimumSystemVersion': '10.13',
|
'LSMinimumSystemVersion': '10.13',
|
||||||
'CFBundleShortVersionString': f"3.14.0.{BUILD_DATE} Beta",
|
'CFBundleShortVersionString': f"3.14.4.{BUILD_DATE} Beta",
|
||||||
'NSHumanReadableCopyright': u"Copyright © 2018-2025, Dmitriy Yefremov",
|
'NSHumanReadableCopyright': u"Copyright © 2018-2026, Dmitriy Yefremov",
|
||||||
'NSRequiresAquaSystemAppearance': 'false',
|
'NSRequiresAquaSystemAppearance': 'false',
|
||||||
'NSHighResolutionCapable': 'true'
|
'NSHighResolutionCapable': 'true'
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ PATH_EXE = [os.path.join(DIR_PATH, EXE_NAME)]
|
|||||||
block_cipher = None
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
excludes = ['app.tools.mpv',
|
excludes = ['gi.repository.Gst',
|
||||||
'gi.repository.Gst',
|
|
||||||
'gi.repository.GstBase',
|
'gi.repository.GstBase',
|
||||||
'gi.repository.GstVideo',
|
'gi.repository.GstVideo',
|
||||||
'youtube_dl',
|
'youtube_dl',
|
||||||
@@ -30,13 +29,13 @@ a = Analysis([EXE_NAME],
|
|||||||
pathex=PATH_EXE,
|
pathex=PATH_EXE,
|
||||||
binaries=[],
|
binaries=[],
|
||||||
datas=ui_files,
|
datas=ui_files,
|
||||||
hiddenimports=['fileinput', 'uuid', 'ctypes.wintypes', 'asyncio'],
|
hiddenimports=['fileinput', 'uuid', 'ctypes.wintypes', 'asyncio', 'getpass'],
|
||||||
hookspath=[],
|
hookspath=[],
|
||||||
runtime_hooks=[],
|
runtime_hooks=[],
|
||||||
hooksconfig={
|
hooksconfig={
|
||||||
"gi": {
|
"gi": {
|
||||||
"languages": ["en", "be", "es", "it", "nl",
|
"languages": ["en", "be", "es", "it", "nl", "pl",
|
||||||
"pl", "pt", "ru", "tr", "zh_CN"],
|
"pt", "ru", "sk", "tr", "zh_CN"],
|
||||||
"module-versions": {
|
"module-versions": {
|
||||||
"Gtk": "3.0",
|
"Gtk": "3.0",
|
||||||
"GtkSource": "3",
|
"GtkSource": "3",
|
||||||
@@ -48,9 +47,19 @@ a = Analysis([EXE_NAME],
|
|||||||
win_private_assemblies=False,
|
win_private_assemblies=False,
|
||||||
cipher=block_cipher,
|
cipher=block_cipher,
|
||||||
noarchive=False)
|
noarchive=False)
|
||||||
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data,
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
cipher=block_cipher)
|
cipher=block_cipher)
|
||||||
|
|
||||||
|
|
||||||
|
splash = Splash('logo.png',
|
||||||
|
binaries=a.binaries,
|
||||||
|
datas=a.datas)
|
||||||
|
|
||||||
|
|
||||||
exe = EXE(pyz,
|
exe = EXE(pyz,
|
||||||
|
splash,
|
||||||
a.scripts,
|
a.scripts,
|
||||||
[],
|
[],
|
||||||
exclude_binaries=True,
|
exclude_binaries=True,
|
||||||
@@ -59,13 +68,16 @@ exe = EXE(pyz,
|
|||||||
bootloader_ignore_signals=False,
|
bootloader_ignore_signals=False,
|
||||||
contents_directory='.',
|
contents_directory='.',
|
||||||
strip=False,
|
strip=False,
|
||||||
upx=True,
|
upx=False,
|
||||||
console=False,
|
console=False,
|
||||||
icon='icon.ico')
|
icon='icon.ico')
|
||||||
|
|
||||||
|
|
||||||
coll = COLLECT(exe,
|
coll = COLLECT(exe,
|
||||||
a.binaries,
|
a.binaries,
|
||||||
a.zipfiles,
|
a.zipfiles,
|
||||||
a.datas,
|
a.datas,
|
||||||
|
splash.binaries,
|
||||||
strip=False,
|
strip=False,
|
||||||
upx=True,
|
upx=True,
|
||||||
upx_exclude=[],
|
upx_exclude=[],
|
||||||
|
|||||||
26
build/win/hide_splash.patch
Normal file
26
build/win/hide_splash.patch
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
Subject: [PATCH] hide_splash
|
||||||
|
---
|
||||||
|
Index: app/ui/main.py
|
||||||
|
IDEA additional info:
|
||||||
|
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||||
|
<+>UTF-8
|
||||||
|
===================================================================
|
||||||
|
diff --git a/app/ui/main.py b/app/ui/main.py
|
||||||
|
--- a/app/ui/main.py (revision 0fc0ef1d3e80fc84f4da81e1117db63a1f1d3467)
|
||||||
|
+++ b/app/ui/main.py (date 1771419933854)
|
||||||
|
@@ -651,6 +651,15 @@
|
||||||
|
gen = self.init_http_api()
|
||||||
|
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||||
|
|
||||||
|
+ if hasattr(sys, "_MEIPASS"):
|
||||||
|
+ import pyi_splash
|
||||||
|
+
|
||||||
|
+ if pyi_splash.is_alive():
|
||||||
|
+ pyi_splash.close()
|
||||||
|
+
|
||||||
|
+ if self._main_window.is_suspended():
|
||||||
|
+ self._main_window.present()
|
||||||
|
+
|
||||||
|
def do_shutdown(self):
|
||||||
|
""" Performs shutdown tasks """
|
||||||
|
if self._settings.load_last_config:
|
||||||
BIN
build/win/logo.png
Executable file
BIN
build/win/logo.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -1,14 +1,17 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import sys
|
||||||
import ssl
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
from multiprocessing import freeze_support
|
if hasattr(sys, "_MEIPASS"):
|
||||||
|
import os
|
||||||
|
import ssl
|
||||||
|
from multiprocessing import freeze_support
|
||||||
|
|
||||||
|
os.environ["PYTHONUTF8"] = "1"
|
||||||
|
freeze_support()
|
||||||
|
# TODO There needs to be a more "correct" way.
|
||||||
|
ssl._create_default_https_context = ssl._create_unverified_context
|
||||||
|
|
||||||
from app.ui.main import start_app
|
from app.ui.main import start_app
|
||||||
|
|
||||||
os.environ["PYTHONUTF8"] = "1"
|
|
||||||
# TODO There needs to be a more "correct" way.
|
|
||||||
ssl._create_default_https_context = ssl._create_unverified_context
|
|
||||||
|
|
||||||
freeze_support()
|
|
||||||
start_app()
|
start_app()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ GenericName[nl]=Enigma2 boeket editor
|
|||||||
GenericName[pl]=Edytor bukietów Enigma2
|
GenericName[pl]=Edytor bukietów Enigma2
|
||||||
GenericName[pt]=Editor de buquês Enigma2
|
GenericName[pt]=Editor de buquês Enigma2
|
||||||
GenericName[ru]=Редактор букетов Enigma2
|
GenericName[ru]=Редактор букетов Enigma2
|
||||||
|
GenericName[sk]=Enigma2 editor balíčkov
|
||||||
GenericName[tr]=Enigma2 buket düzenleyici
|
GenericName[tr]=Enigma2 buket düzenleyici
|
||||||
GenericName[zh_CN]=Enigma2频道编辑器
|
GenericName[zh_CN]=Enigma2频道编辑器
|
||||||
Comment=Channel and satellite list editor for Enigma2
|
Comment=Channel and satellite list editor for Enigma2
|
||||||
@@ -21,6 +22,7 @@ Comment[nl]=Kanaal- en satellietlijsteditor voor Enigma2
|
|||||||
Comment[pl]=Edytor list kanałów i satelitów dla Enigma2
|
Comment[pl]=Edytor list kanałów i satelitów dla Enigma2
|
||||||
Comment[pt]=Editor de lista de canais e satélites para Enigma2
|
Comment[pt]=Editor de lista de canais e satélites para Enigma2
|
||||||
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
Comment[ru]=Редактор списка каналов и спутников для Enigma2
|
||||||
|
Comment[sk]=Editor zoznamu kanálov a satelitov pre Enigma2
|
||||||
Comment[tr]=Enigma2 için Kanal ve uydu listesi düzenleyici
|
Comment[tr]=Enigma2 için Kanal ve uydu listesi düzenleyici
|
||||||
Comment[zh_CN]=Enigma2频道和卫星列表编辑器
|
Comment[zh_CN]=Enigma2频道和卫星列表编辑器
|
||||||
Icon=demon-editor
|
Icon=demon-editor
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2018-2025 Dmitriy Yefremov
|
# Copyright (C) 2018-2026 Dmitriy Yefremov
|
||||||
# This file is distributed under the MIT license.
|
# This file is distributed under the MIT license.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -11,7 +11,7 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "translator-credits"
|
msgid "translator-credits"
|
||||||
msgstr "Dmitriy Yefremov"
|
msgstr "Дзмітрый Яфрэмаў"
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
msgid "Service"
|
msgid "Service"
|
||||||
@@ -1596,3 +1596,21 @@ msgstr "Захаваць бягучыя змены"
|
|||||||
|
|
||||||
msgid "Create a new service"
|
msgid "Create a new service"
|
||||||
msgstr "Стварыць новы сэрвіс"
|
msgstr "Стварыць новы сэрвіс"
|
||||||
|
|
||||||
|
msgid "Extension support is disabled!"
|
||||||
|
msgstr "Падтрымка пашырэнняў адключана!"
|
||||||
|
|
||||||
|
msgid "Do you want to enable it?"
|
||||||
|
msgstr "Жадаеце ўключыць?"
|
||||||
|
|
||||||
|
msgid "Playback of IPTV streams only!"
|
||||||
|
msgstr "Прайграванне толькі IPTV-патокаў!"
|
||||||
|
|
||||||
|
msgid "There are running background tasks!"
|
||||||
|
msgstr "Ідзе выкананне фонавых задач!"
|
||||||
|
|
||||||
|
msgid "Check if FFmpeg is installed!"
|
||||||
|
msgstr "Праверце, ці ўсталяваны FFmpeg!"
|
||||||
|
|
||||||
|
msgid "It can cause some problems."
|
||||||
|
msgstr "Гэта можа выклікаць некаторыя праблемы."
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Copyright (C) 2018-2025 Dmitriy Yefremov
|
# Copyright (C) 2018-2026 Dmitriy Yefremov
|
||||||
# This file is distributed under the MIT license.
|
# This file is distributed under the MIT license.
|
||||||
#
|
#
|
||||||
# Charly, 2019.
|
# Charly, 2019.
|
||||||
# Dmitriy Yefremov, 2020-2025.
|
# Dmitriy Yefremov, 2020-2026.
|
||||||
# Thomas Schmidt, 2021.
|
# Thomas Schmidt, 2021.
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1610,3 +1610,21 @@ msgstr "Aktuelle Änderungen speichern"
|
|||||||
|
|
||||||
msgid "Create a new service"
|
msgid "Create a new service"
|
||||||
msgstr "Erstellen eines neuen Service"
|
msgstr "Erstellen eines neuen Service"
|
||||||
|
|
||||||
|
msgid "Extension support is disabled!"
|
||||||
|
msgstr "Die Unterstützung für Erweiterungen ist deaktiviert!"
|
||||||
|
|
||||||
|
msgid "Do you want to enable it?"
|
||||||
|
msgstr "Möchtest du aktivieren?"
|
||||||
|
|
||||||
|
msgid "Playback of IPTV streams only!"
|
||||||
|
msgstr "Wiedergabe nur IPTV-Streams!"
|
||||||
|
|
||||||
|
msgid "There are running background tasks!"
|
||||||
|
msgstr "Es laufen Hintergrundprozesse!"
|
||||||
|
|
||||||
|
msgid "Check if FFmpeg is installed!"
|
||||||
|
msgstr "Prüfe ob FFmpeg installiert ist!"
|
||||||
|
|
||||||
|
msgid "It can cause some problems."
|
||||||
|
msgstr "Das kann zu einigen Problemen führen."
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Copyright (C) 2018-2025 Dmitriy Yefremov
|
# Copyright (C) 2018-2026 Dmitriy Yefremov
|
||||||
# This file is distributed under the MIT license.
|
# This file is distributed under the MIT license.
|
||||||
#
|
#
|
||||||
# SPDX-FileCopyrightText: 2022, 2023, 2024, 2025 Massimo Pissarello <mapi68@gmail.com>
|
# SPDX-FileCopyrightText: 2022, 2023, 2024, 2025, 2026 Massimo Pissarello <mapi68@gmail.com>
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"PO-Revision-Date: 2025-08-30 09:41+0200\n"
|
"PO-Revision-Date: 2026-01-30 17:27+0100\n"
|
||||||
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
|
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
|
||||||
"Language-Team: Italian <>\n"
|
"Language-Team: Italian <>\n"
|
||||||
"Language: it\n"
|
"Language: it\n"
|
||||||
@@ -13,7 +13,7 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Lokalize 25.08.0\n"
|
"X-Generator: Lokalize 25.12.1\n"
|
||||||
|
|
||||||
msgid "translator-credits"
|
msgid "translator-credits"
|
||||||
msgstr "Massimo Pissarello\nNicola Fanghella"
|
msgstr "Massimo Pissarello\nNicola Fanghella"
|
||||||
@@ -1660,3 +1660,9 @@ msgstr "Salva le modifiche attuali"
|
|||||||
|
|
||||||
msgid "Create a new service"
|
msgid "Create a new service"
|
||||||
msgstr "Crea un nuovo servizio"
|
msgstr "Crea un nuovo servizio"
|
||||||
|
|
||||||
|
msgid "Extension support is disabled!"
|
||||||
|
msgstr "Il supporto dell'estensione è disabilitato!"
|
||||||
|
|
||||||
|
msgid "Do you want to enable it?"
|
||||||
|
msgstr "Vuoi abilitarlo?"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2018-2025 Dmitriy Yefremov
|
# Copyright (C) 2018-2026 Dmitriy Yefremov
|
||||||
# This file is distributed under the MIT license.
|
# This file is distributed under the MIT license.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
@@ -11,7 +11,7 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
msgid "translator-credits"
|
msgid "translator-credits"
|
||||||
msgstr "Dmitriy Yefremov"
|
msgstr "Дмитрий Ефремов"
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
msgid "Service"
|
msgid "Service"
|
||||||
@@ -1593,3 +1593,21 @@ msgstr "Сохранить текущие изменения"
|
|||||||
|
|
||||||
msgid "Create a new service"
|
msgid "Create a new service"
|
||||||
msgstr "Создать новый сервис"
|
msgstr "Создать новый сервис"
|
||||||
|
|
||||||
|
msgid "Extension support is disabled!"
|
||||||
|
msgstr "Поддержка расширений отключена!"
|
||||||
|
|
||||||
|
msgid "Do you want to enable it?"
|
||||||
|
msgstr "Желаете включить?"
|
||||||
|
|
||||||
|
msgid "Playback of IPTV streams only!"
|
||||||
|
msgstr "Воспроизведение только IPTV-потоков!"
|
||||||
|
|
||||||
|
msgid "There are running background tasks!"
|
||||||
|
msgstr "Идет выполнение фоновых задач!"
|
||||||
|
|
||||||
|
msgid "Check if FFmpeg is installed!"
|
||||||
|
msgstr "Проверьте, установлен ли FFmpeg!"
|
||||||
|
|
||||||
|
msgid "It can cause some problems."
|
||||||
|
msgstr "Это может вызывать некоторые проблемы."
|
||||||
|
|||||||
1603
po/sk/demon-editor.po
Normal file
1603
po/sk/demon-editor.po
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ msgstr ""
|
|||||||
"Project-Id-Version: DemonEditor\n"
|
"Project-Id-Version: DemonEditor\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-04-16 15:59+0300\n"
|
"POT-Creation-Date: 2020-04-16 15:59+0300\n"
|
||||||
"PO-Revision-Date: 2025-08-24 12:50+0300\n"
|
"PO-Revision-Date: 2026-01-25 17:07+0300\n"
|
||||||
"Last-Translator: audi06_19 <info@dreamosat-forum.com>\n"
|
"Last-Translator: audi06_19 <info@dreamosat-forum.com>\n"
|
||||||
"Language-Team: audi06_19 <info@dreamosat-forum.com>\n"
|
"Language-Team: audi06_19 <info@dreamosat-forum.com>\n"
|
||||||
"Language: tr\n"
|
"Language: tr\n"
|
||||||
@@ -11,7 +11,7 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Poedit 3.7\n"
|
"X-Generator: Poedit 3.8\n"
|
||||||
|
|
||||||
msgid "translator-credits"
|
msgid "translator-credits"
|
||||||
msgstr "audi06_19 <info@dreamosat-forum.com>"
|
msgstr "audi06_19 <info@dreamosat-forum.com>"
|
||||||
@@ -1627,3 +1627,9 @@ msgstr "Mevcut değişiklikleri kaydet"
|
|||||||
|
|
||||||
msgid "Create a new service"
|
msgid "Create a new service"
|
||||||
msgstr "Yeni bir hizmet oluşturun"
|
msgstr "Yeni bir hizmet oluşturun"
|
||||||
|
|
||||||
|
msgid "Extension support is disabled!"
|
||||||
|
msgstr "Uzantı desteği devre dışı bırakıldı!"
|
||||||
|
|
||||||
|
msgid "Do you want to enable it?"
|
||||||
|
msgstr "Etkinleştirmek ister misiniz?"
|
||||||
|
|||||||
Reference in New Issue
Block a user