mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2026-05-09 01:25:50 +02:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a5884cc9a | ||
|
|
115237a10f | ||
|
|
994bd0ee1c | ||
|
|
27e5b373a3 | ||
|
|
43c05b1739 | ||
|
|
bd96c286e9 | ||
|
|
1bded41eab | ||
|
|
5f0f51679c | ||
|
|
380bb3150b | ||
|
|
d1a7a486a2 | ||
|
|
1be167bec3 | ||
|
|
dd3e88589c | ||
|
|
c5a2df6d7d | ||
|
|
c9fc3803c7 | ||
|
|
6afd518cfc | ||
|
|
02a51c9b56 | ||
|
|
c96cfa0e1b | ||
|
|
08bc4ff4c4 | ||
|
|
ae2b78e990 | ||
|
|
88be9fe49c | ||
|
|
9dae9b7219 | ||
|
|
f296a6c90b | ||
|
|
0486776d83 | ||
|
|
3e6146d825 | ||
|
|
9b97341e70 | ||
|
|
41714136e6 | ||
|
|
f781cbb9f6 | ||
|
|
177be7679b | ||
|
|
a65914a48c | ||
|
|
e3ffc2e24b | ||
|
|
79415c69c5 | ||
|
|
54c4e02cee | ||
|
|
f580c5d83c | ||
|
|
e0cbdb2f8d | ||
|
|
839855c076 | ||
|
|
87db39590b | ||
|
|
ebe58903e5 | ||
|
|
ff8d4e5321 | ||
|
|
907415a2c9 | ||
|
|
f9afffbdb3 | ||
|
|
326856b1e3 | ||
|
|
865a326fe9 | ||
|
|
dd2661c6c9 | ||
|
|
623e5a17f5 | ||
|
|
18c1fa736b | ||
|
|
0e6142c751 | ||
|
|
c274c265c6 | ||
|
|
777c09c9b8 | ||
|
|
12a68a4dbb | ||
|
|
eab869d4d5 | ||
|
|
aec76eec45 | ||
|
|
bdab316ba7 | ||
|
|
771ecb696f | ||
|
|
8993fbed5d | ||
|
|
f3cad81a7d | ||
|
|
c1cf343f69 | ||
|
|
f998f66a35 | ||
|
|
9eb4cdc574 | ||
|
|
20120e0db4 | ||
|
|
3446bb225c | ||
|
|
191975bd14 | ||
|
|
f4be52a202 | ||
|
|
ba2272cf13 | ||
|
|
7bd3fcd9a6 | ||
|
|
e54719ca2c | ||
|
|
0c1c44c866 | ||
|
|
06b82251ef | ||
|
|
fb929ec723 | ||
|
|
fd1c1bfd6e | ||
|
|
c48a08b239 | ||
|
|
b647b0a338 | ||
|
|
9b608eeb74 | ||
|
|
07e55b3f1e | ||
|
|
7e639f5637 | ||
|
|
5570d47cae | ||
|
|
5c49c0d123 | ||
|
|
ee6dd511b5 | ||
|
|
1f847233b3 | ||
|
|
835e1af8e4 | ||
|
|
50e0d8b66a | ||
|
|
fb0789664a | ||
|
|
5dd39492f2 | ||
|
|
33be9f21a2 | ||
|
|
7acc9ae74f | ||
|
|
d492022232 | ||
|
|
b034995130 | ||
|
|
f037b3554d | ||
|
|
7f1f27da57 | ||
|
|
d7f3afecb0 | ||
|
|
f309005c52 | ||
|
|
25661816e7 | ||
|
|
a2652cef4b | ||
|
|
adbc9ad322 | ||
|
|
2dc8611294 | ||
|
|
72bfd21056 | ||
|
|
65ef018f81 | ||
|
|
1236c5ebc9 |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.pyc
|
||||
*.pyo
|
||||
*__pycache__
|
||||
.idea
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2023 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
|
||||
|
||||
@@ -26,12 +26,13 @@ Experimental support of Neutrino-MP or others on the same basis (BPanther, etc).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141684475-4511ea4f-b152-42d5-b9c8-f3e1e9a160d0.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141684475-4511ea4f-b152-42d5-b9c8-f3e1e9a160d0.png)
|
||||
* Ability to view EPG and manage timers (via HTTP API).
|
||||
* Simple FTP client (experimental).
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png)
|
||||
[<img src="https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png" width="480"/>](https://user-images.githubusercontent.com/7511379/141681165-5679c331-72e7-4044-b365-dcdb30b1433c.png)
|
||||
|
||||
**To increase program functionality you can use [extensions](https://github.com/DYefremov/demoneditor-extensions).**
|
||||
|
||||
#### Keyboard shortcuts
|
||||
* **Ctrl + X** - only in bouquet list.
|
||||
* **Ctrl + C** - only in services list.
|
||||
Clipboard is **"rubber"**. There is an accumulation before the insertion!
|
||||
* **Ctrl + C** - only in services list.
|
||||
* **Ctrl + Insert** - copies the selected channels from the main list to the bouquet
|
||||
beginning or inserts (creates) a new bouquet.
|
||||
* **Ctrl + BackSpace** - copies the selected channels from the main list to the bouquet end.
|
||||
@@ -109,7 +110,7 @@ just load your data via *"File/Open"* and press *"Save"*. When importing separat
|
||||
|
||||
**The built-in Telnet client does not support ANSI escape sequences!**
|
||||
|
||||
For streams playback, this app supports [VLC](https://www.videolan.org/vlc/), [MPV](https://mpv.io/) and [GStreamer](https://gstreamer.freedesktop.org/). Depending on your distro, you may need to install additional packages and libraries.
|
||||
For streams playback, this app supports [VLC](https://www.videolan.org/vlc/), [MPV](https://mpv.io/) and [GStreamer](https://gstreamer.freedesktop.org/). Depending on your distro, you may need to install additional packages and libraries.
|
||||
#### Command line arguments:
|
||||
* **-l** - write logs to file.
|
||||
* **-d on/off** - turn on/off debug mode. Allows to display more information in the logs.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -68,10 +68,9 @@ def write_bouquet(path, bq, s_type):
|
||||
write_bouquet(path, bq)
|
||||
|
||||
|
||||
@run_task
|
||||
def write_bouquets(path, bouquets, s_type, force_bq_names=False):
|
||||
def write_bouquets(path, bouquets, s_type, force_bq_names=False, blacklist=None):
|
||||
if s_type is SettingsType.ENIGMA_2:
|
||||
BouquetsWriter(path, bouquets, force_bq_names).write()
|
||||
BouquetsWriter(path, bouquets, force_bq_names, blacklist).write()
|
||||
elif s_type is SettingsType.NEUTRINO_MP:
|
||||
write_neutrino_bouquets(path, bouquets)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -47,16 +47,19 @@ class BouquetsWriter:
|
||||
If "force_bq_names" then naming the files using the name of the bouquet.
|
||||
Some images may have problems displaying the favorites list!
|
||||
"""
|
||||
_SERVICE = '#SERVICE 1:7:{}:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.{}.{}" ORDER BY bouquet\n'
|
||||
_SERVICE = '#SERVICE 1:{}:{}:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.{}.{}" ORDER BY bouquet\n'
|
||||
_MARKER = "#SERVICE 1:64:{:X}:0:0:0:0:0:0:0::{}\n"
|
||||
_SPACE = "#SERVICE 1:832:D:{}:0:0:0:0:0:0:\n"
|
||||
_LOCKED = '1:{}:{}:0:0:0:0:0:0:0:FROM BOUQUET "userbouquet.{}.{}" ORDER BY bouquet'
|
||||
_ALT = '#SERVICE 1:134:1:0:0:0:0:0:0:0:FROM BOUQUET "{}" ORDER BY bouquet\n'
|
||||
_ALT_PAT = r"[<>:\"/\\|?*\-\s]"
|
||||
|
||||
def __init__(self, path, bouquets, force_bq_names=False):
|
||||
def __init__(self, path, bouquets, force_bq_names=False, blacklist=None):
|
||||
self._path = path
|
||||
self._bouquets = bouquets
|
||||
self._force_bq_names = force_bq_names
|
||||
self._black_list = set() if blacklist is None else blacklist
|
||||
|
||||
self._marker_index = 1
|
||||
self._space_index = 0
|
||||
self._alt_names = set()
|
||||
@@ -96,7 +99,13 @@ class BouquetsWriter:
|
||||
self.write_sub_bouquet(self._path, bq_name, bq, bqs.type)
|
||||
else:
|
||||
self.write_bouquet(f"{self._path}userbouquet.{bq_name}.{bqs.type}", bq.name, bq.services)
|
||||
line.append(self._SERVICE.format(2 if bqs.type == BqType.RADIO.value else 1, bq_name, bqs.type))
|
||||
bq_type = 2 if bqs.type == BqType.RADIO.value else 1
|
||||
# Parental lock.
|
||||
locked = self._LOCKED.format(ServiceType.SERVICE, bq_type, bq_name, bqs.type)
|
||||
self._black_list.add(locked) if bq.locked else self._black_list.discard(locked)
|
||||
# Hiding.
|
||||
s_type = ServiceType.HIDDEN if bq.hidden else ServiceType.BOUQUET
|
||||
line.append(self._SERVICE.format(s_type, bq_type, bq_name, bqs.type))
|
||||
|
||||
with open(f"{self._path}bouquets.{bqs.type}", "w", encoding="utf-8", newline="\n") as file:
|
||||
file.writelines(line)
|
||||
@@ -156,15 +165,19 @@ class ServiceType(Enum):
|
||||
SERVICE = "0"
|
||||
BOUQUET = "7" # Sub bouquet.
|
||||
MARKER = "64"
|
||||
SPACE = "832" # Hidden marker.
|
||||
SPACE = "832"
|
||||
ALT = "134" # Alternatives.
|
||||
UDP = "256"
|
||||
HIDDEN = "519" # Skip, hide.
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value):
|
||||
log("Error. No matching service type [{} {}] was found.".format(cls.__name__, value))
|
||||
return cls.SERVICE
|
||||
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
|
||||
class BouquetsReader:
|
||||
""" Class for reading and parsing bouquets. """
|
||||
@@ -197,6 +210,8 @@ class BouquetsReader:
|
||||
for line in file.readlines():
|
||||
if "#SERVICE" in line:
|
||||
name = re.match(self._BQ_PAT, line)
|
||||
s_data = line.split(":")
|
||||
s_type = ServiceType(s_data[1])
|
||||
if name:
|
||||
prefix, b_name = name.group(1), name.group(2)
|
||||
if b_name in b_names:
|
||||
@@ -211,11 +226,14 @@ class BouquetsReader:
|
||||
rb_name = f"{rb_name} {real_b_names[rb_name]}"
|
||||
else:
|
||||
real_b_names[rb_name] = 0
|
||||
# Locked, hidden.
|
||||
s_data[:2] = "10"
|
||||
locked = ":".join(s_data).rstrip()
|
||||
hidden = s_type is ServiceType.HIDDEN
|
||||
|
||||
bouquets[2].append(Bouquet(rb_name, bq_type, services, None, None, b_name))
|
||||
bouquets[2].append(Bouquet(rb_name, bq_type, services, locked, hidden, b_name))
|
||||
else:
|
||||
s_data = line.split(":")
|
||||
if len(s_data) == 12 and s_data[1] == ServiceType.MARKER.value:
|
||||
if len(s_data) == 12 and s_type is ServiceType.MARKER:
|
||||
b_name = f"{_MARKER_PREFIX}{s_data[-1].strip()}"
|
||||
bouquets[2].append(Bouquet(b_name, BqType.MARKER.value, [], None, None, line.strip()))
|
||||
else:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -38,7 +38,7 @@ from app.ui.uicommons import IPTV_ICON
|
||||
|
||||
# url, description, urlkey, account, usrname, psw, s_type, iconsrc, iconsrc_b, group
|
||||
NEUTRINO_FAV_ID_FORMAT = "{}::{}::{}::{}::{}::{}::{}::{}::{}::{}"
|
||||
ENIGMA2_FAV_ID_FORMAT = " {}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION {}\n"
|
||||
ENIGMA2_FAV_ID_FORMAT = " {}:{}:{:X}:{:X}:{:X}:{:X}:{:X}:0:0:0:{}:{}\n#DESCRIPTION {}\n"
|
||||
MARKER_FORMAT = " 1:64:{}:0:0:0:0:0:0:0::{}\n#DESCRIPTION {}\n"
|
||||
PICON_FORMAT = "{}_{}_{:X}_{:X}_{:X}_{:X}_{:X}_0_0_0.png"
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -31,7 +31,6 @@ import os
|
||||
from app.eparser.iptv import NEUTRINO_FAV_ID_FORMAT
|
||||
from app.eparser.neutrino import KSP, SP, get_xml_attributes, get_attributes, API_VER
|
||||
from app.eparser.neutrino.nxml import XmlHandler, NeutrinoDocument
|
||||
from app.ui.uicommons import LOCKED_ICON, HIDE_ICON
|
||||
from ..ecommons import Bouquets, Bouquet, BouquetService, BqServiceType, PROVIDER, BqType
|
||||
|
||||
_FILE = "bouquets.xml"
|
||||
@@ -72,8 +71,8 @@ def parse_bouquets(file, name, bq_type):
|
||||
bouquets[2].append(Bouquet(name=bq_name,
|
||||
type=bq_type,
|
||||
services=services,
|
||||
locked=LOCKED_ICON if locked == "1" else None,
|
||||
hidden=HIDE_ICON if hidden == "1" else None,
|
||||
locked=locked == "1",
|
||||
hidden=hidden == "1",
|
||||
file=SP.join("{}{}{}".format(k, KSP, v) for k, v in bq_attrs.items())))
|
||||
|
||||
if BqType(bq_type) is BqType.BOUQUET:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -48,6 +48,8 @@ IS_DARWIN = sys.platform == "darwin"
|
||||
IS_WIN = sys.platform == "win32"
|
||||
IS_LINUX = sys.platform == "linux"
|
||||
|
||||
USE_HEADER_BAR = int(bool(os.environ.get("GNOME_DESKTOP_SESSION_ID")))
|
||||
|
||||
|
||||
class Defaults(Enum):
|
||||
""" Default program settings """
|
||||
@@ -67,7 +69,9 @@ class Defaults(Enum):
|
||||
"/media/hdd/picon/",
|
||||
"/media/usb/picon/",
|
||||
"/media/mmc/picon/",
|
||||
"/media/cf/picon/")
|
||||
"/media/cf/picon/",
|
||||
"/hdd/picon/",
|
||||
"/usb/picon/")
|
||||
# Neutrino.
|
||||
NEUTRINO_BOX_SERVICES_PATH = "/var/tuxbox/config/zapit/"
|
||||
NEUTRINO_BOX_SATELLITE_PATH = "/var/tuxbox/config/"
|
||||
@@ -81,6 +85,8 @@ class Defaults(Enum):
|
||||
BACKUP_BEFORE_DOWNLOADING = True
|
||||
BACKUP_BEFORE_SAVE = True
|
||||
V5_SUPPORT = False
|
||||
UNLIMITED_COPY_BUFFER = False
|
||||
EXTENSIONS_SUPPORT = False
|
||||
FORCE_BQ_NAMES = False
|
||||
HTTP_API_SUPPORT = True
|
||||
ENABLE_YT_DL = False
|
||||
@@ -423,7 +429,7 @@ class Settings:
|
||||
|
||||
@default_data_path.setter
|
||||
def default_data_path(self, value):
|
||||
self._settings["default_data_path"] = value
|
||||
self._settings["default_data_path"] = Settings.normalize_path(value)
|
||||
|
||||
@property
|
||||
def default_backup_path(self):
|
||||
@@ -431,7 +437,7 @@ class Settings:
|
||||
|
||||
@default_backup_path.setter
|
||||
def default_backup_path(self, value):
|
||||
self._settings["default_backup_path"] = value
|
||||
self._settings["default_backup_path"] = Settings.normalize_path(value)
|
||||
|
||||
@property
|
||||
def default_picon_path(self):
|
||||
@@ -439,7 +445,7 @@ class Settings:
|
||||
|
||||
@default_picon_path.setter
|
||||
def default_picon_path(self, value):
|
||||
self._settings["default_picon_path"] = value
|
||||
self._settings["default_picon_path"] = Settings.normalize_path(value)
|
||||
|
||||
@property
|
||||
def profile_data_path(self):
|
||||
@@ -475,7 +481,7 @@ class Settings:
|
||||
|
||||
@recordings_path.setter
|
||||
def recordings_path(self, value):
|
||||
self._settings["recordings_path"] = value
|
||||
self._settings["recordings_path"] = Settings.normalize_path(value)
|
||||
|
||||
# ******** Streaming ********* #
|
||||
|
||||
@@ -606,6 +612,22 @@ class Settings:
|
||||
def v5_support(self, value):
|
||||
self._settings["v5_support"] = value
|
||||
|
||||
@property
|
||||
def unlimited_copy_buffer(self):
|
||||
return self._settings.get("unlimited_copy_buffer", Defaults.UNLIMITED_COPY_BUFFER.value)
|
||||
|
||||
@unlimited_copy_buffer.setter
|
||||
def unlimited_copy_buffer(self, value):
|
||||
self._settings["unlimited_copy_buffer"] = value
|
||||
|
||||
@property
|
||||
def extensions_support(self):
|
||||
return self._settings.get("extensions_support", Defaults.EXTENSIONS_SUPPORT.value)
|
||||
|
||||
@extensions_support.setter
|
||||
def extensions_support(self, value):
|
||||
self._settings["extensions_support"] = value
|
||||
|
||||
@property
|
||||
def force_bq_names(self):
|
||||
return self._settings.get("force_bq_names", Defaults.FORCE_BQ_NAMES.value)
|
||||
@@ -682,6 +704,14 @@ class Settings:
|
||||
|
||||
# *********** Appearance *********** #
|
||||
|
||||
@property
|
||||
def use_header_bar(self):
|
||||
return self._settings.get("use_header_bar", USE_HEADER_BAR)
|
||||
|
||||
@use_header_bar.setter
|
||||
def use_header_bar(self, value):
|
||||
self._settings["use_header_bar"] = value
|
||||
|
||||
@property
|
||||
def list_font(self):
|
||||
return self._settings.get("list_font", "")
|
||||
@@ -916,6 +946,10 @@ class Settings:
|
||||
with open(CONFIG_FILE, "w", encoding="utf-8") as config_file:
|
||||
json.dump(config, config_file, indent=" ")
|
||||
|
||||
@staticmethod
|
||||
def normalize_path(path):
|
||||
return f"{os.path.normpath(path)}{SEP}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -29,15 +29,16 @@
|
||||
""" Module for working with epg.dat file. """
|
||||
import abc
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import struct
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
from collections import namedtuple
|
||||
from datetime import datetime, timezone
|
||||
from tempfile import NamedTemporaryFile
|
||||
from urllib.parse import urlparse
|
||||
from xml.dom.minidom import parse, Node, Document
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import requests
|
||||
|
||||
@@ -54,8 +55,8 @@ except ModuleNotFoundError:
|
||||
else:
|
||||
DETECT_ENCODING = True
|
||||
|
||||
EpgEvent = namedtuple("EpgEvent", ["service_name", "title", "time", "desc", "event_data"])
|
||||
EpgEvent.__new__.__defaults__ = ("N/A", "N/A", "N/A", "N/A", None) # For Python3 < 3.7
|
||||
EpgEvent = namedtuple("EpgEvent", ["service_name", "title", "start", "end", "length", "desc", "event_data"])
|
||||
EpgEvent.__new__.__defaults__ = ("N/A", "N/A", 0, 0, 0, "N/A", None) # For Python3 < 3.7
|
||||
|
||||
|
||||
class Reader(metaclass=abc.ABCMeta):
|
||||
@@ -298,14 +299,15 @@ class XmlTvReader(Reader):
|
||||
offset = datetime.now() - dt
|
||||
|
||||
for srv in filter(lambda s: any(name in names for name in s.names), self._ids.values()):
|
||||
ev = list(filter(lambda s: s.start < utc, srv.events))
|
||||
ev = max(filter(lambda s: s.start < utc, srv.events), key=lambda x: x.start, default=None)
|
||||
if ev:
|
||||
ev = ev[-1]
|
||||
start = datetime.fromtimestamp(ev.start) + offset
|
||||
end_time = datetime.fromtimestamp(ev.duration) + offset
|
||||
tm = f"{start.strftime('%H:%M')} - {end_time.strftime('%H:%M')}"
|
||||
start = start.timestamp()
|
||||
end_time = end_time.timestamp()
|
||||
|
||||
for n in srv.names:
|
||||
events[n] = EpgEvent(n, ev.title, tm, ev.desc, ev)
|
||||
events[n] = EpgEvent(n, ev.title, start, end_time, int(ev.duration), ev.desc, ev)
|
||||
|
||||
return events
|
||||
|
||||
@@ -373,32 +375,39 @@ class ChannelsParser:
|
||||
refs = []
|
||||
dom = parse(path)
|
||||
description = "".join(n.data + "\n" for n in dom.childNodes if n.nodeType == Node.COMMENT_NODE)
|
||||
pos_pat = re.compile(r"^\d+\.\d+[EW]$")
|
||||
|
||||
for elem in dom.getElementsByTagName("channels"):
|
||||
c_count = 0
|
||||
comment_count = 0
|
||||
current_data = ""
|
||||
data = ""
|
||||
ch_id = None
|
||||
pos = None
|
||||
ch_type = BqServiceType.DEFAULT
|
||||
|
||||
if elem.hasChildNodes():
|
||||
for n in elem.childNodes:
|
||||
if n.nodeType == Node.ELEMENT_NODE:
|
||||
ch_id = n.getAttribute("id")
|
||||
|
||||
if n.nodeType == Node.COMMENT_NODE:
|
||||
c_count += 1
|
||||
comment_count += 1
|
||||
txt = n.data.strip()
|
||||
|
||||
if re.match(pos_pat, txt):
|
||||
pos = txt
|
||||
|
||||
if comment_count:
|
||||
comment_count -= 1
|
||||
else:
|
||||
ref_data = current_data.split(":")
|
||||
refs.append(BouquetService(name=txt,
|
||||
type=BqServiceType.DEFAULT,
|
||||
data="{}:{}:{}:{}".format(*ref_data[3:7]).upper(),
|
||||
num="{}:{}:{}".format(*ref_data[3:6]).upper()))
|
||||
refs.append(BouquetService(name=txt, type=ch_type, data=data.upper(), num=(pos, ch_id)))
|
||||
|
||||
if n.hasChildNodes():
|
||||
for s_node in n.childNodes:
|
||||
if s_node.nodeType == Node.TEXT_NODE:
|
||||
comment_count -= 1
|
||||
current_data = s_node.data
|
||||
data = s_node.data
|
||||
return refs, description
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -63,6 +63,7 @@ class Player(Gtk.DrawingArea):
|
||||
parent = widget.get_parent()
|
||||
parent.connect("play", self.on_play)
|
||||
parent.connect("stop", self.on_stop)
|
||||
parent.connect("pause", self.on_pause)
|
||||
self.show()
|
||||
|
||||
def get_play_mode(self):
|
||||
@@ -107,6 +108,9 @@ class Player(Gtk.DrawingArea):
|
||||
def on_stop(self, widget, state):
|
||||
self.stop()
|
||||
|
||||
def on_pause(self, widget, state):
|
||||
self.pause()
|
||||
|
||||
def on_release(self, widget, state):
|
||||
self.release()
|
||||
|
||||
@@ -241,7 +245,7 @@ class MpvPlayer(Player):
|
||||
self._is_playing = True
|
||||
|
||||
def pause(self):
|
||||
pass
|
||||
self._player.pause = not self._player.pause
|
||||
|
||||
def set_time(self, time):
|
||||
pass
|
||||
@@ -330,7 +334,11 @@ class GstPlayer(Player):
|
||||
self._is_playing = False
|
||||
|
||||
def pause(self):
|
||||
self._player.set_state(self.STATE.PAUSED)
|
||||
state = self._player.get_state(self.STATE.NULL).state
|
||||
if state == self.STATE.PLAYING:
|
||||
self._player.set_state(self.STATE.PAUSED)
|
||||
elif state == self.STATE.PAUSED:
|
||||
self._player.set_state(self.STATE.PLAYING)
|
||||
|
||||
def set_time(self, time):
|
||||
pass
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -429,7 +429,7 @@ class SatellitesParser(HTMLParser):
|
||||
class ServicesParser(HTMLParser):
|
||||
""" Services parser for LYNGSAT source. """
|
||||
|
||||
def __init__(self, source=SatelliteSource.LYNGSAT, entities=False, separator=' '):
|
||||
def __init__(self, source=SatelliteSource.LYNGSAT, entities=False, separator=' ', lang=None):
|
||||
|
||||
HTMLParser.__init__(self)
|
||||
|
||||
@@ -452,6 +452,12 @@ class ServicesParser(HTMLParser):
|
||||
self._KING_TR_PAT = re.compile((r"(DVB-S[2]?)\s?(?:T2-MI,\s+PLP\s+(\d+))?.*"
|
||||
r"?(?:PLS:\s+(Root|Gold|Combo)\+(\d+))?"
|
||||
r"\s+(.*PSK).*?(?:.*Stream\s+(\d+))?.*"))
|
||||
self._lang = "en"
|
||||
if lang:
|
||||
langs = {"en", "fr", "nl", "de", "se", "no", "pt", "es", "it", "pl",
|
||||
"cz", "gr", "fi", "ar", "tr", "ru", "sc", "ro", "hu", "sq"}
|
||||
lang, _, _ = lang.partition("_")
|
||||
self._lang = lang if lang in langs else self._lang
|
||||
|
||||
self._parse_html_entities = entities
|
||||
self._separator = separator
|
||||
@@ -580,7 +586,7 @@ class ServicesParser(HTMLParser):
|
||||
if len(r) == 13 and SatellitesParser.POS_PAT.match(r[0].text):
|
||||
t_cell = r[4]
|
||||
if t_cell.url and t_cell.url.startswith("tp.php?tp="):
|
||||
t_cell.url = f"https://en.kingofsat.net/{t_cell.url}"
|
||||
t_cell.url = f"https://{self._lang}.kingofsat.net/{t_cell.url}"
|
||||
t_cell.text = f"{r[2].text} {r[3].text} {r[6].text} {r[8].text}"
|
||||
trs.append(t_cell)
|
||||
return trs
|
||||
@@ -730,11 +736,12 @@ class ServicesParser(HTMLParser):
|
||||
|
||||
s_type = self._S_TYPES.get(s_type, "3")
|
||||
_s_type = SERVICE_TYPE.get(s_type, SERVICE_TYPE.get("3"))
|
||||
reg, grp = r[3].text, r[4].text
|
||||
|
||||
name, pkg, cas, sid, v_pid, a_pid = r[2].text, r[5].text, r[6].text, r[7].text, None, None
|
||||
flags, sid, fav_id, picon_id, data_id = self.get_service_data(s_type, pkg, sid, tid, nid, nsp,
|
||||
v_pid, a_pid, cas, use_pids)
|
||||
services.append(Service(flags, "s", None, name, None, None, pkg, _s_type, None, picon_id,
|
||||
services.append(Service(flags, "s", None, name, reg, grp, pkg, _s_type, None, picon_id,
|
||||
sid, str(freq), sr, pol, fec, sys, pos, data_id, fav_id, multi_tr or tr))
|
||||
|
||||
return services
|
||||
@@ -743,9 +750,9 @@ class ServicesParser(HTMLParser):
|
||||
""" Returns converted transponder data. """
|
||||
sys = get_key_by_value(SYSTEM, sys)
|
||||
mod = get_key_by_value(MODULATION, mod)
|
||||
fec = get_key_by_value(FEC, fec)
|
||||
fec = get_key_by_value(FEC, fec) or "0"
|
||||
# For negative (West) positions: 3600 - numeric position value!!!
|
||||
namespace = f"{3600 - pos if pos < 0 else pos:04x}0000"
|
||||
namespace = f"{3600 - abs(pos) if pos < 0 else pos:04x}0000"
|
||||
tr_flag = 1
|
||||
roll_off = 0 # 35% DVB-S2/DVB-S (default)
|
||||
pilot = 2 # Auto
|
||||
|
||||
@@ -75,13 +75,13 @@
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Settings</attribute>
|
||||
<attribute name="action">app.on_settings</attribute>
|
||||
<attribute name="action">app.preferences</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Exit</attribute>
|
||||
<attribute name="action">app.on_close_app</attribute>
|
||||
<attribute name="action">app.quit</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
@@ -156,6 +156,11 @@
|
||||
<attribute name="label" translatable="yes">Alternate layout</attribute>
|
||||
<attribute name="action">app.set_alternate_layout</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Alternate window title</attribute>
|
||||
<attribute name="action">app.set_alternate_title</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu id="tools_menu">
|
||||
@@ -176,6 +181,8 @@
|
||||
<attribute name="action">app.on_logs_show</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section id="extension_section">
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">FTP client</attribute>
|
||||
@@ -191,7 +198,7 @@
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">About</attribute>
|
||||
<attribute name="action">app.on_about_app</attribute>
|
||||
<attribute name="action">app.about</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
@@ -200,19 +207,19 @@
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">About</attribute>
|
||||
<attribute name="action">app.on_about_app</attribute>
|
||||
<attribute name="action">app.about</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Settings</attribute>
|
||||
<attribute name="action">app.on_settings</attribute>
|
||||
<attribute name="action">app.preferences</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Exit</attribute>
|
||||
<attribute name="action">app.on_close_app</attribute>
|
||||
<attribute name="action">app.quit</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
@@ -370,6 +377,11 @@
|
||||
<attribute name="label" translatable="yes">Alternate layout</attribute>
|
||||
<attribute name="action">app.set_alternate_layout</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">Alternate window title</attribute>
|
||||
<attribute name="action">app.set_alternate_title</attribute>
|
||||
<attribute name="hidden-when">action-disabled</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
@@ -390,6 +402,8 @@
|
||||
<attribute name="action">app.on_logs_show</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section id="mac_extension_section">
|
||||
</section>
|
||||
</submenu>
|
||||
<submenu>
|
||||
<attribute name="label" translatable="yes">FTP client</attribute>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -39,7 +39,7 @@ from app.commons import run_idle, get_size_from_bytes
|
||||
from app.settings import SettingsType, SEP
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_builder
|
||||
from app.ui.main_helper import append_text_to_tview
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, IS_GNOME_SESSION
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, HeaderBar
|
||||
|
||||
|
||||
class RestoreType(Enum):
|
||||
@@ -77,8 +77,8 @@ class BackupDialog:
|
||||
self._message_label = builder.get_object("message_label")
|
||||
self._file_count_label = builder.get_object("file_count_label")
|
||||
|
||||
if IS_GNOME_SESSION:
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar()
|
||||
self._dialog_window.set_titlebar(header_bar)
|
||||
|
||||
button_box = builder.get_object("main_button_box")
|
||||
|
||||
@@ -40,7 +40,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon_name">system-help</property>
|
||||
<property name="type_hint">normal</property>
|
||||
<property name="program_name">DemonEditor</property>
|
||||
<property name="version">3.2.3 Beta</property>
|
||||
<property name="version">3.5.0 Beta</property>
|
||||
<property name="copyright">2018-2023 Dmitriy Yefremov
|
||||
</property>
|
||||
<property name="comments" translatable="yes">Enigma2 channel and satellite list editor.</property>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -34,8 +34,8 @@ from functools import lru_cache
|
||||
from pathlib import Path
|
||||
|
||||
from app.commons import run_idle
|
||||
from app.settings import SEP, IS_WIN
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN, IS_GNOME_SESSION
|
||||
from app.settings import SEP, IS_WIN, USE_HEADER_BAR
|
||||
from .uicommons import Gtk, UI_RESOURCES_PATH, TEXT_DOMAIN
|
||||
|
||||
|
||||
class Dialog(Enum):
|
||||
@@ -157,7 +157,7 @@ def get_file_chooser_dialog(transient, text, settings, action_type, file_filter,
|
||||
|
||||
|
||||
def get_input_dialog(transient, text):
|
||||
builder, dialog = get_dialog_from_xml(DialogType.INPUT, transient, use_header=IS_GNOME_SESSION)
|
||||
builder, dialog = get_dialog_from_xml(DialogType.INPUT, transient, use_header=USE_HEADER_BAR)
|
||||
entry = builder.get_object("input_entry")
|
||||
entry.set_text(text if text else "")
|
||||
response = dialog.run()
|
||||
@@ -223,9 +223,9 @@ def get_builder(path, handlers=None, use_str=False, objects=None, tag="property"
|
||||
|
||||
if use_str:
|
||||
if objects:
|
||||
builder.add_objects_from_string(get_dialogs_string(path, tag).format(use_header=IS_GNOME_SESSION), objects)
|
||||
builder.add_objects_from_string(get_dialogs_string(path, tag).format(use_header=USE_HEADER_BAR), objects)
|
||||
else:
|
||||
builder.add_from_string(get_dialogs_string(path, tag).format(use_header=IS_GNOME_SESSION))
|
||||
builder.add_from_string(get_dialogs_string(path, tag).format(use_header=USE_HEADER_BAR))
|
||||
else:
|
||||
if objects:
|
||||
builder.add_objects_from_string(get_dialogs_string(path, tag), objects)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -43,13 +43,13 @@ from gi.repository import GLib
|
||||
from app.commons import run_idle, run_task, run_with_delay
|
||||
from app.connections import download_data, DownloadType, HttpAPI
|
||||
from app.eparser.ecommons import BouquetService, BqServiceType
|
||||
from app.settings import SEP, EpgSource
|
||||
from app.settings import SEP, EpgSource, IS_WIN
|
||||
from app.tools.epg import EPG, ChannelsParser, EpgEvent, XmlTvReader
|
||||
from app.ui.dialogs import get_message, show_dialog, DialogType, get_builder
|
||||
from app.ui.tasks import BGTaskWidget
|
||||
from app.ui.timers import TimerTool
|
||||
from ..main_helper import on_popup_menu, update_entry_data, scroll_to
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey, IS_GNOME_SESSION, Page
|
||||
from ..main_helper import on_popup_menu, update_entry_data, scroll_to, update_toggle_model, update_filter_sat_positions
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, Column, EPG_ICON, KeyboardKey, Page, HeaderBar
|
||||
|
||||
|
||||
class RefsSource(Enum):
|
||||
@@ -73,13 +73,14 @@ class EpgCache(dict):
|
||||
|
||||
self.init()
|
||||
|
||||
@run_idle
|
||||
@run_with_delay(5)
|
||||
def init(self):
|
||||
if self._src is EpgSource.XML:
|
||||
url = self._settings.epg_xml_source
|
||||
gz_file = f"{self._settings.profile_data_path}epg{os.sep}epg.gz"
|
||||
self._reader = XmlTvReader(gz_file, url)
|
||||
|
||||
@run_with_delay(2)
|
||||
def process_data():
|
||||
t = BGTaskWidget(self._app, "Processing XMLTV data...", self._reader.parse, )
|
||||
self._app.emit("add-background-task", t)
|
||||
@@ -196,13 +197,15 @@ class EpgSettingsPopover(Gtk.Popover):
|
||||
|
||||
|
||||
class EpgTool(Gtk.Box):
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, app, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self._current_bq = None
|
||||
self._app = app
|
||||
self._app.connect("fav-changed", self.on_service_changed)
|
||||
self._app.connect("profile-changed", self.on_profile_changed)
|
||||
self._app.connect("bouquet-changed", self.on_bouquet_changed)
|
||||
self._app.connect("filter-toggled", self.on_filter_toggled)
|
||||
|
||||
handlers = {"on_epg_press": self.on_epg_press,
|
||||
"on_timer_add": self.on_timer_add,
|
||||
@@ -217,12 +220,24 @@ class EpgTool(Gtk.Box):
|
||||
self._model = builder.get_object("epg_model")
|
||||
self._filter_model = builder.get_object("epg_filter_model")
|
||||
self._filter_model.set_visible_func(self.epg_filter_function)
|
||||
self._filter_button = builder.get_object("epg_filter_button")
|
||||
self._filter_entry = builder.get_object("epg_filter_entry")
|
||||
self._multi_epg_button = builder.get_object("multi_epg_button")
|
||||
self._event_count_label = builder.get_object("event_count_label")
|
||||
self.pack_start(builder.get_object("epg_frame"), True, True, 0)
|
||||
# Custom sort function.
|
||||
self._view.get_model().set_sort_func(2, self.time_sort_func, 2)
|
||||
# Custom data functions.
|
||||
renderer = builder.get_object("epg_start_renderer")
|
||||
column = builder.get_object("epg_start_column")
|
||||
column.set_cell_data_func(renderer, self.start_data_func)
|
||||
renderer = builder.get_object("epg_end_renderer")
|
||||
column = builder.get_object("epg_end_column")
|
||||
column.set_cell_data_func(renderer, self.end_data_func)
|
||||
renderer = builder.get_object("epg_length_renderer")
|
||||
column = builder.get_object("epg_length_column")
|
||||
column.set_cell_data_func(renderer, self.duration_data_func)
|
||||
# Time formats.
|
||||
self._time_fmt = "%a %x - %H:%M"
|
||||
self._duration_fmt = f"%{'' if IS_WIN else '-'}Hh %Mm"
|
||||
|
||||
self.show()
|
||||
|
||||
@@ -279,29 +294,40 @@ class EpgTool(Gtk.Box):
|
||||
self._app.wait_dialog.show()
|
||||
self._app.send_http_request(HttpAPI.Request.EPG, quote(ref), self.update_epg_data)
|
||||
|
||||
def on_profile_changed(self, app, prf):
|
||||
self.update_epg_data()
|
||||
|
||||
@run_idle
|
||||
def update_epg_data(self, epg):
|
||||
def update_epg_data(self, epg=None):
|
||||
self._event_count_label.set_text("0")
|
||||
self._model.clear()
|
||||
list(map(self._model.append, (self.get_event(e) for e in epg.get("event_list", [])
|
||||
if e.get("e2eventid", "").isdigit())))
|
||||
if epg:
|
||||
list(map(self._model.append, (self.get_event(e) for e in epg.get("event_list", [])
|
||||
if e.get("e2eventid", "").isdigit())))
|
||||
self._event_count_label.set_text(str(len(self._model)))
|
||||
self._app.wait_dialog.hide()
|
||||
|
||||
@staticmethod
|
||||
def get_event(event, show_day=True):
|
||||
t_str = f"{'%a, ' if show_day else ''}%x, %H:%M"
|
||||
s_name = event.get("e2eventservicename", "")
|
||||
title = event.get("e2eventtitle", "") or ""
|
||||
desc = event.get("e2eventdescription", "") or ""
|
||||
desc = desc.strip()
|
||||
start, duration = int(event.get("e2eventstart", "0")), int(event.get("e2eventduration", "0"))
|
||||
|
||||
start = int(event.get("e2eventstart", "0"))
|
||||
start_time = datetime.fromtimestamp(start)
|
||||
end_time = datetime.fromtimestamp(start + int(event.get("e2eventduration", "0")))
|
||||
ev_time = f"{start_time.strftime(t_str)} - {end_time.strftime('%H:%M')}"
|
||||
return EpgEvent(s_name, title, start, start + duration, duration, desc, event)
|
||||
|
||||
return EpgEvent(s_name, title, ev_time, desc, event)
|
||||
def start_data_func(self, column, renderer, model, itr, data):
|
||||
value = datetime.fromtimestamp(model.get_value(itr, Column.EPG_START))
|
||||
renderer.set_property("text", value.strftime(self._time_fmt))
|
||||
|
||||
def end_data_func(self, column, renderer, model, itr, data):
|
||||
value = datetime.fromtimestamp(model.get_value(itr, Column.EPG_END))
|
||||
renderer.set_property("text", value.strftime(self._time_fmt))
|
||||
|
||||
def duration_data_func(self, column, renderer, model, itr, data):
|
||||
value = datetime.utcfromtimestamp(model.get_value(itr, Column.EPG_LENGTH))
|
||||
renderer.set_property("text", value.strftime(self._duration_fmt))
|
||||
|
||||
def on_epg_filter_changed(self, entry):
|
||||
self._filter_model.refilter()
|
||||
@@ -312,14 +338,17 @@ class EpgTool(Gtk.Box):
|
||||
|
||||
def epg_filter_function(self, model, itr, data):
|
||||
txt = self._filter_entry.get_text().upper()
|
||||
return next((s for s in model.get(itr, 0, 1, 2, 3) if txt in s.upper()), False)
|
||||
return next((s for s in model.get(itr,
|
||||
Column.EPG_SERVICE,
|
||||
Column.EPG_TITLE,
|
||||
Column.EPG_DESC) if txt in s.upper()), False)
|
||||
|
||||
def time_sort_func(self, model, iter1, iter2, column):
|
||||
""" Custom sort function for time column. """
|
||||
event1 = model.get_value(iter1, 4)
|
||||
event2 = model.get_value(iter2, 4)
|
||||
|
||||
return int(event1.get("e2eventstart", "0")) - int(event2.get("e2eventstart", "0"))
|
||||
def on_filter_toggled(self, app, value):
|
||||
if self._app.page is Page.EPG:
|
||||
active = not self._filter_button.get_active()
|
||||
self._filter_button.set_active(active)
|
||||
if active:
|
||||
self._filter_entry.grab_focus()
|
||||
|
||||
def on_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
dst = view.get_dest_row_at_pos(x, y)
|
||||
@@ -373,6 +402,7 @@ class EpgDialog:
|
||||
"on_save_to_xml": self.on_save_to_xml,
|
||||
"on_auto_configuration": self.on_auto_configuration,
|
||||
"on_filter_toggled": self.on_filter_toggled,
|
||||
"on_filter_satellite_toggled": self.on_filter_satellite_toggled,
|
||||
"on_filter_changed": self.on_filter_changed,
|
||||
"on_info_bar_close": self.on_info_bar_close,
|
||||
"on_popup_menu": on_popup_menu,
|
||||
@@ -392,7 +422,8 @@ class EpgDialog:
|
||||
"on_update_on_start_switch": self.on_update_on_start_switch,
|
||||
"on_field_icon_press": self.on_field_icon_press,
|
||||
"on_key_press": self.on_key_press,
|
||||
"on_bq_cursor_changed": self.on_bq_cursor_changed}
|
||||
"on_bq_cursor_changed": self.on_bq_cursor_changed,
|
||||
"on_source_view_query_tooltip": self.on_source_view_query_tooltip}
|
||||
|
||||
self._app = app
|
||||
self._ex_services = self._app.current_services
|
||||
@@ -405,6 +436,7 @@ class EpgDialog:
|
||||
self._update_epg_data_on_start = False
|
||||
self._refs_source = RefsSource.SERVICES
|
||||
self._download_xml_is_active = False
|
||||
self._sat_positions = None
|
||||
|
||||
builder = get_builder(f"{UI_RESOURCES_PATH}epg{SEP}dialog.glade", handlers)
|
||||
|
||||
@@ -419,12 +451,14 @@ class EpgDialog:
|
||||
self._assign_ref_popup_item = builder.get_object("bouquet_assign_ref_popup_item")
|
||||
self._left_action_box = builder.get_object("left_action_box")
|
||||
self._xml_download_progress_bar = builder.get_object("xml_download_progress_bar")
|
||||
self._src_load_spinner = builder.get_object("src_load_spinner")
|
||||
# Filter
|
||||
self._filter_bar = builder.get_object("filter_bar")
|
||||
self._filter_entry = builder.get_object("filter_entry")
|
||||
self._filter_auto_switch = builder.get_object("filter_auto_switch")
|
||||
self._services_filter_model = builder.get_object("services_filter_model")
|
||||
self._services_filter_model.set_visible_func(self.services_filter_function)
|
||||
self._sat_pos_filter_model = builder.get_object("sat_pos_filter_model")
|
||||
# Info
|
||||
self._source_count_label = builder.get_object("source_count_label")
|
||||
self._source_info_label = builder.get_object("source_info_label")
|
||||
@@ -443,9 +477,8 @@ class EpgDialog:
|
||||
self._update_on_start_switch = builder.get_object("update_on_start_switch")
|
||||
self._epg_dat_source_box = builder.get_object("epg_dat_source_box")
|
||||
|
||||
if IS_GNOME_SESSION:
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True, title="EPG",
|
||||
subtitle=get_message("List configuration"))
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar(title="EPG", subtitle=get_message("List configuration"))
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
builder.get_object("left_action_box").reparent(header_bar)
|
||||
right_box = builder.get_object("right_action_box")
|
||||
@@ -528,6 +561,8 @@ class EpgDialog:
|
||||
return
|
||||
yield True
|
||||
|
||||
self._src_load_spinner.start()
|
||||
|
||||
if self._refs_source is RefsSource.SERVICES:
|
||||
yield from self.init_lamedb_source(refs)
|
||||
elif self._refs_source is RefsSource.XML:
|
||||
@@ -538,6 +573,8 @@ class EpgDialog:
|
||||
self.show_info_message(str(e), Gtk.MessageType.ERROR)
|
||||
else:
|
||||
self.show_info_message("Unknown names source!", Gtk.MessageType.ERROR)
|
||||
|
||||
self._src_load_spinner.stop()
|
||||
yield True
|
||||
|
||||
def init_bouquet_data(self):
|
||||
@@ -555,7 +592,7 @@ class EpgDialog:
|
||||
|
||||
factor = self._app.DEL_FACTOR / 4
|
||||
for index, srv in enumerate(filtered):
|
||||
self._services_model.append((srv.service, srv.pos, srv.fav_id, srv.picon_id))
|
||||
self._services_model.append((srv.service, srv.pos, srv.fav_id, srv.picon_id, srv.picon_id))
|
||||
if index % factor == 0:
|
||||
yield True
|
||||
|
||||
@@ -627,12 +664,20 @@ class EpgDialog:
|
||||
except Exception as e:
|
||||
raise ValueError(f"{get_message('XML parsing error:')} {e}")
|
||||
else:
|
||||
if refs:
|
||||
s_refs = filter(lambda x: x.num in refs, s_refs)
|
||||
|
||||
refs = refs or {}
|
||||
factor = self._app.DEL_FACTOR / 4
|
||||
|
||||
for index, srv in enumerate(s_refs):
|
||||
self._services_model.append((srv.name, " ", srv.data, ""))
|
||||
ref_data = srv.data.split(":")
|
||||
ref = ":".join(ref_data[3:6])
|
||||
if ref in refs:
|
||||
continue
|
||||
|
||||
data = ":".join(ref_data[3:7])
|
||||
pos, ch_id = srv.num
|
||||
pos = pos or " "
|
||||
self._services_model.append((srv.name, pos, data, "_".join(ref_data).rstrip("_"), ch_id))
|
||||
|
||||
if index % factor == 0:
|
||||
yield True
|
||||
|
||||
@@ -660,6 +705,25 @@ class EpgDialog:
|
||||
if path:
|
||||
self._filter_entry.set_text(model[path][Column.FAV_SERVICE] or "")
|
||||
|
||||
def on_source_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip):
|
||||
result = view.get_dest_row_at_pos(x, y)
|
||||
if not result:
|
||||
return False
|
||||
|
||||
path, pos = result
|
||||
ch_id = view.get_model()[path][-1]
|
||||
if not ch_id:
|
||||
return False
|
||||
|
||||
if self._refs_source is RefsSource.XML:
|
||||
text = f"ID = {ch_id}"
|
||||
else:
|
||||
text = f"{get_message('Service reference')}: {ch_id.rstrip('.png')}"
|
||||
|
||||
tooltip.set_text(text)
|
||||
view.set_tooltip_row(tooltip, path)
|
||||
return True
|
||||
|
||||
@run_idle
|
||||
def on_save_to_xml(self, item):
|
||||
response = show_dialog(DialogType.CHOOSER, self._dialog, settings=self._settings)
|
||||
@@ -692,7 +756,7 @@ class EpgDialog:
|
||||
tr = {ord(k): ord(v) for k, v in zip(*symbols)}
|
||||
|
||||
source = {}
|
||||
for row in self._services_model:
|
||||
for row in self._source_view.get_model():
|
||||
name = re.sub("\\W+", "", str(row[0])).upper()
|
||||
name = name.translate(tr) if use_cyrillic else name
|
||||
source[name] = row
|
||||
@@ -737,11 +801,11 @@ class EpgDialog:
|
||||
|
||||
fav_id = row[Column.FAV_ID]
|
||||
fav_id_data = fav_id.split(":")
|
||||
fav_id_data[3:7] = data[-2].split(":")
|
||||
fav_id_data[3:7] = data[-3].split(":")
|
||||
|
||||
if data[-1]:
|
||||
row[Column.FAV_POS] = data[-1]
|
||||
p_data = data[-1].split("_")
|
||||
if data[-2]:
|
||||
row[Column.FAV_POS] = data[-2]
|
||||
p_data = data[-2].split("_")
|
||||
if p_data:
|
||||
fav_id_data[2] = p_data[2]
|
||||
|
||||
@@ -755,16 +819,28 @@ class EpgDialog:
|
||||
|
||||
def on_filter_toggled(self, button):
|
||||
self._filter_bar.set_visible(button.get_active())
|
||||
if not button.get_active():
|
||||
self._filter_entry.set_text("")
|
||||
if button.get_active():
|
||||
self._sat_positions = {r[1] for r in self._services_model}
|
||||
update_filter_sat_positions(self._sat_pos_filter_model, self._sat_positions)
|
||||
else:
|
||||
self._sat_positions = None
|
||||
self._filter_entry.set_text("") if self._filter_entry.get_text() else self.on_filter_changed()
|
||||
|
||||
@run_with_delay(1)
|
||||
def on_filter_changed(self, entry):
|
||||
def on_filter_satellite_toggled(self, toggle, path):
|
||||
update_toggle_model(self._sat_pos_filter_model, path, toggle)
|
||||
self._sat_positions.clear()
|
||||
self._sat_positions.update({r[0] for r in self._sat_pos_filter_model if r[1]})
|
||||
self.on_filter_changed()
|
||||
|
||||
@run_with_delay(2)
|
||||
def on_filter_changed(self, entry=None):
|
||||
self._services_filter_model.refilter()
|
||||
|
||||
def services_filter_function(self, model, itr, data):
|
||||
txt = self._filter_entry.get_text().upper()
|
||||
return model is None or model == "None" or txt in model.get_value(itr, 0).upper()
|
||||
pos = model.get_value(itr, 1)
|
||||
pos = self._sat_positions is None or pos in self._sat_positions
|
||||
return model is None or model == "None" or (txt in model.get_value(itr, 0).upper() and pos)
|
||||
|
||||
def on_info_bar_close(self, bar=None, resp=None):
|
||||
self._info_bar.set_visible(False)
|
||||
@@ -806,7 +882,6 @@ class EpgDialog:
|
||||
def update_source_info(self, info):
|
||||
lines = info.split("\n")
|
||||
self._source_info_label.set_text(lines[0] if lines else "")
|
||||
self._source_view.set_tooltip_text(info)
|
||||
|
||||
@run_idle
|
||||
def update_source_count_info(self):
|
||||
|
||||
@@ -40,8 +40,12 @@ Author: Dmitriy Yefremov
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name title -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name time -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name start -->
|
||||
<column type="gint"/>
|
||||
<!-- column-name end -->
|
||||
<column type="gint"/>
|
||||
<!-- column-name length -->
|
||||
<column type="gint"/>
|
||||
<!-- column-name description -->
|
||||
<column type="gchararray"/>
|
||||
<!-- column-name data -->
|
||||
@@ -57,7 +61,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkFrame" id="epg_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.49000000953674316</property>
|
||||
<property name="label-xalign">0.49</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="epg_box">
|
||||
@@ -277,7 +281,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="fixed-height-mode">True</property>
|
||||
<property name="rubber-banding">True</property>
|
||||
<property name="enable-grid-lines">both</property>
|
||||
<property name="tooltip-column">3</property>
|
||||
<property name="tooltip-column">6</property>
|
||||
<signal name="button-press-event" handler="on_epg_press" swapped="no"/>
|
||||
<signal name="query-tooltip" handler="on_view_query_tooltip" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
@@ -293,7 +297,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="fixed-width">100</property>
|
||||
<property name="min-width">40</property>
|
||||
<property name="title" translatable="yes">Service</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_service_renderer">
|
||||
@@ -309,10 +313,9 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkTreeViewColumn" id="epg_title_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="fixed-width">170</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="min-width">150</property>
|
||||
<property name="title" translatable="yes">Title</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_title_renderer">
|
||||
@@ -325,18 +328,18 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_time_column">
|
||||
<object class="GtkTreeViewColumn" id="epg_start_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="fixed-width">210</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">Time</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<property name="title" translatable="yes">Start time</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_time_renderer">
|
||||
<object class="GtkCellRendererText" id="epg_start_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<property name="xalign">0.49000000953674316</property>
|
||||
<property name="xalign">0.49</property>
|
||||
<property name="width-chars">27</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
@@ -345,20 +348,59 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_desc_column">
|
||||
<object class="GtkTreeViewColumn" id="epg_end_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">End time</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">3</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_end_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<property name="xalign">0.49</property>
|
||||
<property name="width-chars">27</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_length_column">
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="fixed-width">100</property>
|
||||
<property name="min-width">50</property>
|
||||
<property name="title" translatable="yes">Length</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">4</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_length_renderer">
|
||||
<property name="xpad">5</property>
|
||||
<property name="xalign">0.49</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="epg_desc_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min-width">100</property>
|
||||
<property name="title" translatable="yes">Description</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<property name="sort-column-id">3</property>
|
||||
<property name="alignment">0.49</property>
|
||||
<property name="sort-column-id">5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="epg_desc_renderer">
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">3</attribute>
|
||||
<attribute name="text">5</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -42,10 +42,10 @@ from gi.repository import GLib
|
||||
|
||||
from app.commons import log, run_task, run_idle, get_size_from_bytes
|
||||
from app.connections import UtfFTP
|
||||
from app.settings import IS_LINUX, IS_DARWIN, IS_WIN, SEP
|
||||
from app.settings import IS_LINUX, IS_DARWIN, IS_WIN, SEP, USE_HEADER_BAR
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_builder, get_message
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, IS_GNOME_SESSION, Page
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK, Page
|
||||
|
||||
File = namedtuple("File", ["icon", "name", "size", "date", "attr", "extra"])
|
||||
|
||||
@@ -529,7 +529,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
|
||||
@run_idle
|
||||
def show_edit_dialog(self, f_path, data):
|
||||
dialog = TextEditDialog(f_path, IS_GNOME_SESSION)
|
||||
dialog = TextEditDialog(f_path, USE_HEADER_BAR)
|
||||
dialog.text = data
|
||||
ok = Gtk.ResponseType.OK
|
||||
if dialog.run() == ok and show_dialog(DialogType.QUESTION, self._app.app_window) == ok:
|
||||
@@ -578,7 +578,7 @@ class FtpClientBox(Gtk.HBox):
|
||||
log(f"Init attributes error [{attrs}]. Invalid length!")
|
||||
return
|
||||
|
||||
dialog = AttributesDialog(attrs, IS_GNOME_SESSION)
|
||||
dialog = AttributesDialog(attrs, USE_HEADER_BAR)
|
||||
ok = Gtk.ResponseType.OK
|
||||
if dialog.run() == ok and show_dialog(DialogType.QUESTION, self._app.app_window) == ok:
|
||||
log(self._ftp.sendcmd(f"SITE CHMOD {dialog.permissions} {file}"))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2023 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
|
||||
@@ -27,11 +27,12 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface domain="demon-editor">
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellite list editor. -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkListStore" id="bq_list_store">
|
||||
<columns>
|
||||
@@ -54,6 +55,113 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon-name">document-revert-symbolic-rtl</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkPopover" id="options_popover">
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="options_popover_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">10</property>
|
||||
<property name="margin-end">10</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="replace_existing_settings_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive" bind-source="bouquets_only_switch" bind-property="active" bind-flags="invert-boolean">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Enables overwriting existing main list services.</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="replace_existing_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Replace existing</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="replace_existing_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="bouquets_settings_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Enables skipping services import from lamedb.</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="bouquets_only_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Bouquets data only</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="bouquets_only_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
<signal name="state-set" handler="on_bouquets_only_switch" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkModelButton">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="text" translatable="yes">Close</property>
|
||||
<property name="centered">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
@@ -217,7 +325,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Bouquets and services</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="image">import_image</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<signal name="clicked" handler="on_import" swapped="no"/>
|
||||
@@ -229,20 +336,81 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="details_button">
|
||||
<property name="label" translatable="yes">Details</property>
|
||||
<object class="GtkBox" id="extra_header_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="focus-on-click">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="image">details_image</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="details_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="focus-on-click">False</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Details</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="image">details_image</property>
|
||||
<property name="always-show-image">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="options_menu_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="direction">none</property>
|
||||
<property name="popover">options_popover</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="options_nutton_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="options_button_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Options</property>
|
||||
<property name="icon-name">applications-system-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="options_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Options</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -37,7 +37,7 @@ from app.eparser.neutrino.bouquets import parse_webtv, parse_bouquets as get_neu
|
||||
from app.settings import SettingsType, IS_DARWIN, SEP
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_chooser_dialog, get_message, get_builder
|
||||
from app.ui.main_helper import on_popup_menu, get_iptv_data
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, Column, IS_GNOME_SESSION, Page
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, Column, Page, HeaderBar
|
||||
|
||||
|
||||
def import_bouquet(app, model, path, appender, file_path=None):
|
||||
@@ -121,6 +121,7 @@ class ImportDialog:
|
||||
"on_resize": self.on_resize,
|
||||
"on_main_paned_realize": self.on_main_paned_realize,
|
||||
"on_visible_page": self.on_visible_page,
|
||||
"on_bouquets_only_switch": self.on_bouquets_only_switch,
|
||||
"on_key_press": self.on_key_press}
|
||||
|
||||
builder = get_builder(f"{UI_RESOURCES_PATH}imports.glade", handlers)
|
||||
@@ -144,6 +145,10 @@ class ImportDialog:
|
||||
self._dialog_window.set_transient_for(app.app_window)
|
||||
self._info_bar = builder.get_object("info_bar")
|
||||
self._message_label = builder.get_object("message_label")
|
||||
# Options.
|
||||
self._replace_existing_switch = builder.get_object("replace_existing_switch")
|
||||
self._bouquets_only_switch = builder.get_object("bouquets_only_switch")
|
||||
self._bouquets_settings_box = builder.get_object("bouquets_settings_box")
|
||||
# Bouquets page.
|
||||
self._bq_model = builder.get_object("bq_list_store")
|
||||
self._bq_view = builder.get_object("bq_view")
|
||||
@@ -158,19 +163,20 @@ class ImportDialog:
|
||||
self._sat_model = builder.get_object("sat_list_store")
|
||||
self._sat_count_label = builder.get_object("sat_count_label")
|
||||
|
||||
if IS_GNOME_SESSION:
|
||||
if self._settings.use_header_bar:
|
||||
actions_box = builder.get_object("actions_box")
|
||||
builder.get_object("toolbar_box").set_visible(False)
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
header_bar = HeaderBar()
|
||||
stack_switcher = builder.get_object("stack_switcher")
|
||||
actions_box.remove(stack_switcher)
|
||||
header_bar.set_custom_title(stack_switcher)
|
||||
button = builder.get_object("import_button")
|
||||
actions_box.remove(button)
|
||||
header_bar.pack_start(button)
|
||||
button = builder.get_object("details_button")
|
||||
actions_box.remove(button)
|
||||
header_bar.pack_end(button)
|
||||
extra_box = builder.get_object("extra_header_box")
|
||||
actions_box.remove(extra_box)
|
||||
header_bar.pack_end(extra_box)
|
||||
|
||||
self._dialog_window.set_titlebar(header_bar)
|
||||
|
||||
window_size = self._settings.get("import_dialog_window_size")
|
||||
@@ -258,17 +264,26 @@ class ImportDialog:
|
||||
with suppress(ValueError):
|
||||
bq.remove(b)
|
||||
|
||||
services = list(filter(lambda s: s.fav_id not in self._ids, services))
|
||||
if self._bouquets_only_switch.get_active():
|
||||
services = ()
|
||||
else:
|
||||
services = list(filter(lambda s: s.fav_id not in self._ids, services))
|
||||
|
||||
self._append(self._bouquets, services)
|
||||
|
||||
if self._replace_existing_switch.get_active():
|
||||
self._app.emit("services_update", {s.fav_id: s for s in filter(lambda s: s.fav_id in self._ids, services)})
|
||||
|
||||
self._dialog_window.destroy()
|
||||
|
||||
def import_satellites_data(self):
|
||||
if show_dialog(DialogType.QUESTION, self._dialog_window) != Gtk.ResponseType.OK:
|
||||
return
|
||||
|
||||
replace_existing = False
|
||||
replace_existing = self._replace_existing_switch.get_active()
|
||||
services = []
|
||||
current_services = self._app.current_services
|
||||
to_replace = {}
|
||||
|
||||
for row in self._sat_model:
|
||||
if row[-1]:
|
||||
@@ -277,10 +292,14 @@ class ImportDialog:
|
||||
for s in filter(lambda srv: srv.fav_id not in skip, self._sat_services.get(sat[0], ())):
|
||||
if replace_existing and s.fav_id in self._ids:
|
||||
current_services[s.fav_id] = s
|
||||
to_replace[s.fav_id] = s
|
||||
elif s.fav_id not in self._ids:
|
||||
services.append(s)
|
||||
|
||||
self._append((), services)
|
||||
if to_replace:
|
||||
self._app.emit("services_update", to_replace)
|
||||
|
||||
self._dialog_window.destroy()
|
||||
|
||||
@run_idle
|
||||
@@ -398,6 +417,11 @@ class ImportDialog:
|
||||
|
||||
def on_visible_page(self, stack, param):
|
||||
self._page = Page(stack.get_visible_child_name())
|
||||
self._bouquets_settings_box.set_sensitive(self._page is Page.SERVICES)
|
||||
|
||||
def on_bouquets_only_switch(self, switch, state):
|
||||
if state:
|
||||
self._replace_existing_switch.set_active(False)
|
||||
|
||||
def on_key_press(self, view, event):
|
||||
""" Handling keystrokes """
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2023 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
|
||||
@@ -27,11 +27,11 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.16"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-description Enigma2 channel and satellites list editor for GNU/Linux. -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
@@ -1435,7 +1435,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">yt_receive_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_receive" swapped="no"/>
|
||||
<accelerator key="d" signal="clicked"/>
|
||||
<accelerator key="d" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1456,7 +1456,7 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="yt_impotr_box">
|
||||
<object class="GtkButtonBox" id="yt_import_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -45,10 +45,10 @@ from app.settings import SettingsType
|
||||
from app.tools.yt import YouTubeException, YouTube
|
||||
from app.ui.dialogs import Action, show_dialog, DialogType, get_message, get_builder
|
||||
from app.ui.main_helper import get_iptv_url, on_popup_menu, get_picon_pixbuf
|
||||
from app.ui.uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, IPTV_ICON, Column, KeyboardKey, get_yt_icon)
|
||||
from app.ui.uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, IPTV_ICON, Column, KeyboardKey, get_yt_icon, HeaderBar)
|
||||
|
||||
_DIGIT_ENTRY_NAME = "digit-entry"
|
||||
_ENIGMA2_REFERENCE = "{}:{}:{}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
_ENIGMA2_REFERENCE = "{}:{}:{:X}:{:X}:{:X}:{:X}:{:X}:0:0:0"
|
||||
_PATTERN = re.compile("(?:^[\\s]*$|\\D)")
|
||||
_UI_PATH = UI_RESOURCES_PATH + "iptv.glade"
|
||||
|
||||
@@ -197,7 +197,7 @@ class IptvDialog:
|
||||
self.show_info_message("Unknown stream type {}".format(s_type), Gtk.MessageType.ERROR)
|
||||
|
||||
self._srv_id_entry.set_text(data[1])
|
||||
self._srv_type_entry.set_text(data[2])
|
||||
self._srv_type_entry.set_text(str(int(data[2], 16)))
|
||||
self._sid_entry.set_text(str(int(data[3], 16)))
|
||||
self._tr_id_entry.set_text(str(int(data[4], 16)))
|
||||
self._net_id_entry.set_text(str(int(data[5], 16)))
|
||||
@@ -215,7 +215,7 @@ class IptvDialog:
|
||||
self.on_url_changed(self._url_entry)
|
||||
self._reference_entry.set_text(_ENIGMA2_REFERENCE.format(self.get_type(),
|
||||
self._srv_id_entry.get_text(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._srv_type_entry.get_text()),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
int(self._net_id_entry.get_text()),
|
||||
@@ -300,7 +300,7 @@ class IptvDialog:
|
||||
name = self._name_entry.get_text().strip()
|
||||
fav_id = ENIGMA2_FAV_ID_FORMAT.format(self.get_type(),
|
||||
self._srv_id_entry.get_text(),
|
||||
self._srv_type_entry.get_text(),
|
||||
int(self._srv_type_entry.get_text()),
|
||||
int(self._sid_entry.get_text()),
|
||||
int(self._tr_id_entry.get_text()),
|
||||
int(self._net_id_entry.get_text()),
|
||||
@@ -601,7 +601,7 @@ class IptvListConfigurationDialog(IptvListDialog):
|
||||
|
||||
st_type = get_stream_type(self._stream_type_combobox)
|
||||
s_id = "0" if id_default else self._list_srv_id_entry.get_text()
|
||||
srv_type = "1" if type_default else self._list_srv_type_entry.get_text()
|
||||
srv_type = int("1" if type_default else self._list_srv_type_entry.get_text())
|
||||
sid = "0" if sid_auto else self._list_sid_entry.get_text()
|
||||
tid = "0" if tid_default else f"{int(self._list_tid_entry.get_text()):X}"
|
||||
nid = "0" if nid_default else f"{int(self._list_nid_entry.get_text()):X}"
|
||||
@@ -616,12 +616,13 @@ class IptvListConfigurationDialog(IptvListDialog):
|
||||
if all_default:
|
||||
data[1], data[2], data[3], data[4], data[5], data[6] = "010000"
|
||||
else:
|
||||
data[0], data[1], data[2], data[4], data[5], data[6] = st_type, s_id, srv_type, tid, nid, namespace
|
||||
data[0], data[1], data[4], data[5], data[6] = st_type, s_id, tid, nid, namespace
|
||||
data[2] = f"{srv_type:X}"
|
||||
|
||||
data[3] = f"{index:X}" if sid_auto else sid
|
||||
if sid_auto:
|
||||
params[0] = index
|
||||
picon_id = PICON_FORMAT.format(st_type, int(s_id), int(srv_type), *params)
|
||||
picon_id = PICON_FORMAT.format(st_type, int(s_id), srv_type, *params)
|
||||
data = ":".join(data)
|
||||
new_fav_id = f"{data}{sep}{desc}"
|
||||
row[Column.FAV_ID] = new_fav_id
|
||||
@@ -859,7 +860,6 @@ class YtListImportDialog:
|
||||
"on_key_press": self.on_key_press,
|
||||
"on_close": self.on_close}
|
||||
|
||||
# self._main_window, self._settings, self.append_imported_services
|
||||
self.appender = app.append_imported_services
|
||||
self._settings = app.app_settings
|
||||
self._s_type = self._settings.setting_type
|
||||
@@ -891,6 +891,17 @@ class YtListImportDialog:
|
||||
self._import_button.bind_property("sensitive", self._quality_box, "sensitive")
|
||||
self._receive_button.bind_property("sensitive", self._import_button, "sensitive")
|
||||
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar(title="YouTube", subtitle=get_message("Playlist import"))
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
actions_box = builder.get_object("yt_actions_box")
|
||||
import_box = builder.get_object("yt_import_box")
|
||||
actions_box.remove(import_box)
|
||||
header_bar.pack_end(import_box)
|
||||
actions_box.remove(self._receive_button)
|
||||
header_bar.pack_start(self._receive_button)
|
||||
actions_box.set_visible(False)
|
||||
|
||||
window_size = self._settings.get("yt_import_dialog_size")
|
||||
if window_size:
|
||||
self._dialog.resize(*window_size)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -898,7 +898,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="action_name">app.on_settings</property>
|
||||
<property name="action_name">app.preferences</property>
|
||||
<property name="text" translatable="yes">Settings</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -949,7 +949,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">app.on_about_app</property>
|
||||
<property name="action_name">app.about</property>
|
||||
<property name="text" translatable="yes">About</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -963,8 +963,8 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="action_name">app.quit</property>
|
||||
<property name="text" translatable="yes">Exit</property>
|
||||
<signal name="clicked" handler="on_close_app" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1003,6 +1003,11 @@ Author: Dmitriy Yefremov
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-find-and-replace</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_duplicates_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-select-all</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="mark_not_in_bq_image">
|
||||
<property name="visible">True</property>
|
||||
@@ -1607,6 +1612,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkStack" id="stack">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hhomogeneous">False</property>
|
||||
<property name="transition_type">crossfade</property>
|
||||
<signal name="notify::visible-child-name" handler="on_visible_page" swapped="no"/>
|
||||
<child>
|
||||
@@ -1651,7 +1657,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkLabel" id="app_ver_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label">3.2.3 Beta</property>
|
||||
<property name="label">3.5.0 Beta</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
@@ -1845,7 +1851,7 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="enigma_lock_hide_box">
|
||||
<property name="sensitive">False</property>
|
||||
<property name="sensitive" bind-source="bouquet_lock_hide_box" bind-property="sensitive" bind-flags="invert-boolean">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
@@ -3932,6 +3938,7 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="bouquet_lock_hide_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
@@ -4171,7 +4178,7 @@ Author: Dmitriy Yefremov
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">False</property>
|
||||
<property name="shrink">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -4749,6 +4756,17 @@ Author: Dmitriy Yefremov
|
||||
<signal name="activate" handler="on_mark_duplicates" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="fav_remove_dup_popup_item">
|
||||
<property name="label" translatable="yes">Remove duplicates</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">remove_duplicates_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_remove_duplicates" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSeparatorMenuItem" id="fav_popup_separator_3">
|
||||
<property name="visible">True</property>
|
||||
|
||||
331
app/ui/main.py
331
app/ui/main.py
@@ -48,7 +48,7 @@ from app.eparser.enigma.bouquets import BqServiceType
|
||||
from app.eparser.iptv import export_to_m3u, StreamType
|
||||
from app.eparser.neutrino.bouquets import BqType
|
||||
from app.settings import (SettingsType, Settings, SettingsException, SettingsReadException,
|
||||
IS_DARWIN, PlayStreamsMode, IS_LINUX)
|
||||
IS_DARWIN, PlayStreamsMode, IS_LINUX, USE_HEADER_BAR)
|
||||
from app.tools.media import Recorder
|
||||
from app.ui.control import ControlTool
|
||||
from app.ui.epg.epg import EpgCache, EpgSettingsPopover, EpgDialog, EpgTool
|
||||
@@ -69,7 +69,7 @@ from .search import SearchProvider
|
||||
from .service_details_dialog import ServiceDetailsDialog, Action
|
||||
from .settings_dialog import SettingsDialog
|
||||
from .uicommons import (Gtk, Gdk, UI_RESOURCES_PATH, LOCKED_ICON, HIDE_ICON, IPTV_ICON, MOVE_KEYS, KeyboardKey, Column,
|
||||
FavClickMode, MOD_MASK, APP_FONT, Page, IS_GNOME_SESSION)
|
||||
FavClickMode, MOD_MASK, APP_FONT, Page, HeaderBar)
|
||||
from .xml.dialogs import ServicesUpdateDialog
|
||||
from .xml.edit import SatellitesTool
|
||||
|
||||
@@ -98,7 +98,8 @@ class Application(Gtk.Application):
|
||||
_FAV_ELEMENTS = ("fav_cut_popup_item", "fav_paste_popup_item", "fav_locate_popup_item", "fav_iptv_popup_item",
|
||||
"fav_insert_marker_popup_item", "fav_insert_space_popup_item", "fav_edit_sub_menu_popup_item",
|
||||
"fav_edit_popup_item", "fav_picon_popup_item", "fav_copy_popup_item", "fav_add_alt_popup_item",
|
||||
"fav_epg_configuration_popup_item", "fav_mark_dup_popup_item", "fav_reference_popup_item")
|
||||
"fav_epg_configuration_popup_item", "fav_mark_dup_popup_item", "fav_remove_dup_popup_item",
|
||||
"fav_reference_popup_item")
|
||||
|
||||
_BOUQUET_ELEMENTS = ("bouquets_new_popup_item", "bouquets_edit_popup_item", "bouquets_cut_popup_item",
|
||||
"bouquets_copy_popup_item", "bouquets_paste_popup_item", "new_header_button",
|
||||
@@ -111,8 +112,6 @@ class Application(Gtk.Application):
|
||||
_FAV_IPTV_ELEMENTS = ("fav_iptv_popup_item", "import_m3u_header_button", "export_to_m3u_menu_button",
|
||||
"iptv_menu_button")
|
||||
|
||||
_LOCK_HIDE_ELEMENTS = ("enigma_lock_hide_box", "bouquet_lock_hide_box")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, **kwargs)
|
||||
# Adding command line options
|
||||
@@ -176,6 +175,7 @@ class Application(Gtk.Application):
|
||||
"on_fav_press": self.on_fav_press,
|
||||
"on_locate_in_services": self.on_locate_in_services,
|
||||
"on_mark_duplicates": self.on_mark_duplicates,
|
||||
"on_remove_duplicates": self.on_remove_duplicates,
|
||||
"on_services_mark_not_in_bouquets": self.on_services_mark_not_in_bouquets,
|
||||
"on_services_clear_marked": self.on_services_clear_marked,
|
||||
"on_services_clear_new_marked": self.on_services_clear_new_marked,
|
||||
@@ -308,6 +308,8 @@ class Application(Gtk.Application):
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("epg-dat-downloaded", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("services-update", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("iptv-service-edited", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("iptv-service-added", self, GObject.SIGNAL_RUN_LAST,
|
||||
@@ -457,11 +459,12 @@ class Application(Gtk.Application):
|
||||
self._record_image = builder.get_object("record_button_image")
|
||||
# Dynamically active elements depending on the selected view.
|
||||
d_elements = (self._SERVICE_ELEMENTS, self._BOUQUET_ELEMENTS, self._COMMONS_ELEMENTS, self._FAV_ELEMENTS,
|
||||
self._FAV_ENIGMA_ELEMENTS, self._FAV_IPTV_ELEMENTS, self._LOCK_HIDE_ELEMENTS)
|
||||
self._FAV_ENIGMA_ELEMENTS, self._FAV_IPTV_ELEMENTS)
|
||||
self._tool_elements = {k: builder.get_object(k) for k in set(chain.from_iterable(d_elements))}
|
||||
# Lock, Hide.
|
||||
self.bind_property("is-enigma", self._tool_elements.get(self._LOCK_HIDE_ELEMENTS[0]), "visible")
|
||||
self.bind_property("is-enigma", self._tool_elements.get(self._LOCK_HIDE_ELEMENTS[1]), "visible", 4)
|
||||
self._bouquet_lock_hide_box = builder.get_object("bouquet_lock_hide_box")
|
||||
self._bouquets_view.bind_property("is-focus", self._bouquet_lock_hide_box, "sensitive")
|
||||
self.bind_property("is-enigma", builder.get_object("enigma_lock_hide_box"), "visible")
|
||||
# Clear "New" menu item
|
||||
self.bind_property("is-enigma", builder.get_object("services_clear_new_flag_item"), "visible")
|
||||
# Sub-bouquets menu item.
|
||||
@@ -488,6 +491,7 @@ class Application(Gtk.Application):
|
||||
self._logs_box = builder.get_object("logs_box")
|
||||
self._logs_box.pack_start(LogsClient(self), True, True, 0)
|
||||
self._bottom_paned = builder.get_object("bottom_paned")
|
||||
self.connect("services-update", self.on_services_update)
|
||||
# Send/Receive.
|
||||
self.connect("data-receive", self.on_download)
|
||||
self.connect("data-send", self.on_upload)
|
||||
@@ -503,9 +507,11 @@ class Application(Gtk.Application):
|
||||
# Header bar.
|
||||
profile_box = builder.get_object("profile_combo_box")
|
||||
toolbar_box = builder.get_object("toolbar_main_box")
|
||||
if IS_GNOME_SESSION:
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
header_bar.pack_start(builder.get_object("file_header_button"))
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar()
|
||||
if not IS_DARWIN:
|
||||
header_bar.pack_start(builder.get_object("file_header_button"))
|
||||
|
||||
header_bar.pack_start(profile_box)
|
||||
header_bar.pack_start(toolbar_box)
|
||||
header_bar.set_custom_title(builder.get_object("stack_switcher"))
|
||||
@@ -567,12 +573,16 @@ class Application(Gtk.Application):
|
||||
self._epg_menu_button = builder.get_object("epg_menu_button")
|
||||
self._epg_menu_button.connect("realize", lambda b: b.set_popover(EpgSettingsPopover(self)))
|
||||
self.bind_property("is_enigma", self._epg_menu_button, "sensitive")
|
||||
self._epg_start_time_fmt = "%a, %H:%M"
|
||||
self._epg_end_time_fmt = "%H:%M"
|
||||
# Hiding for Neutrino.
|
||||
self.bind_property("is_enigma", builder.get_object("services_button_box"), "visible")
|
||||
# Setting the last size of the window if it was saved.
|
||||
main_window_size = self._settings.get("window_size")
|
||||
if main_window_size:
|
||||
self._main_window.resize(*main_window_size)
|
||||
# Layout.
|
||||
self.init_layout()
|
||||
# Style.
|
||||
style_provider = Gtk.CssProvider()
|
||||
style_provider.load_from_path(UI_RESOURCES_PATH + "style.css")
|
||||
@@ -581,48 +591,19 @@ class Application(Gtk.Application):
|
||||
|
||||
def do_startup(self):
|
||||
Gtk.Application.do_startup(self)
|
||||
# App menu.
|
||||
builder = get_builder(UI_RESOURCES_PATH + "app_menu.ui", tag="attribute")
|
||||
if not IS_GNOME_SESSION:
|
||||
if IS_DARWIN:
|
||||
self.set_app_menu(builder.get_object("mac_app_menu"))
|
||||
self.set_menubar(builder.get_object("mac_menu_bar"))
|
||||
else:
|
||||
self.set_menubar(builder.get_object("menu_bar"))
|
||||
else:
|
||||
tools_menu = builder.get_object("tools_menu")
|
||||
tools_button = Gtk.MenuButton(visible=True, menu_model=tools_menu, direction=Gtk.ArrowType.NONE)
|
||||
tools_button.set_tooltip_text(get_message("Tools"))
|
||||
tools_button.set_image(Gtk.Image.new_from_icon_name("applications-utilities-symbolic", Gtk.IconSize.BUTTON))
|
||||
self.init_app_menu()
|
||||
self.init_actions()
|
||||
self.set_accels()
|
||||
|
||||
view_menu = builder.get_object("view_menu")
|
||||
view_button = Gtk.MenuButton(visible=True, menu_model=view_menu, direction=Gtk.ArrowType.NONE)
|
||||
view_button.set_tooltip_text(get_message("View"))
|
||||
|
||||
box = Gtk.ButtonBox(visible=True, layout_style="expand")
|
||||
box.add(tools_button)
|
||||
box.add(view_button)
|
||||
self._main_window.get_titlebar().pack_end(box)
|
||||
# IPTV menu.
|
||||
self._iptv_menu_button.set_menu_model(builder.get_object("iptv_menu"))
|
||||
iptv_elem = self._tool_elements.get("fav_iptv_popup_item")
|
||||
for h in (self.on_iptv, self.on_import_yt_list, self.on_import_m3u, self.on_export_iptv_to_m3u,
|
||||
self.on_epg_list_configuration, self.on_iptv_list_configuration, self.on_remove_all_unavailable):
|
||||
iptv_elem.bind_property("sensitive", self.set_action(h.__name__, h, False), "enabled")
|
||||
self.init_drag_and_drop()
|
||||
self.init_appearance()
|
||||
self.filter_set_default()
|
||||
|
||||
def do_activate(self):
|
||||
self._main_window.set_application(self)
|
||||
self._main_window.set_wmclass("DemonEditor", "DemonEditor")
|
||||
self._main_window.present()
|
||||
|
||||
self.init_actions()
|
||||
self.set_accels()
|
||||
self.init_layout()
|
||||
|
||||
self.init_drag_and_drop()
|
||||
self.init_appearance()
|
||||
self.filter_set_default()
|
||||
|
||||
self.init_profiles()
|
||||
gen = self.init_http_api()
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
@@ -666,15 +647,77 @@ class Application(Gtk.Application):
|
||||
self.activate()
|
||||
return 0
|
||||
|
||||
def init_app_menu(self):
|
||||
builder = get_builder(UI_RESOURCES_PATH + "app_menu.ui", tag="attribute")
|
||||
if not USE_HEADER_BAR:
|
||||
if IS_DARWIN:
|
||||
if not self.get_app_menu():
|
||||
self.set_app_menu(builder.get_object("mac_app_menu"))
|
||||
self.set_menubar(builder.get_object("mac_menu_bar"))
|
||||
else:
|
||||
self.set_menubar(builder.get_object("menu_bar"))
|
||||
else:
|
||||
tools_menu = builder.get_object("tools_menu")
|
||||
tools_button = Gtk.MenuButton(visible=True, menu_model=tools_menu, direction=Gtk.ArrowType.NONE)
|
||||
tools_button.set_tooltip_text(get_message("Tools"))
|
||||
tools_button.set_image(Gtk.Image.new_from_icon_name("applications-utilities-symbolic", Gtk.IconSize.BUTTON))
|
||||
|
||||
view_menu = builder.get_object("view_menu")
|
||||
view_button = Gtk.MenuButton(visible=True, menu_model=view_menu, direction=Gtk.ArrowType.NONE)
|
||||
view_button.set_tooltip_text(get_message("View"))
|
||||
|
||||
box = Gtk.ButtonBox(visible=True, layout_style="expand")
|
||||
box.add(tools_button)
|
||||
box.add(view_button)
|
||||
self._main_window.get_titlebar().pack_end(box)
|
||||
# IPTV menu.
|
||||
self._iptv_menu_button.set_menu_model(builder.get_object("iptv_menu"))
|
||||
iptv_elem = self._tool_elements.get("fav_iptv_popup_item")
|
||||
for h in (self.on_iptv, self.on_import_yt_list, self.on_import_m3u, self.on_export_iptv_to_m3u,
|
||||
self.on_epg_list_configuration, self.on_iptv_list_configuration, self.on_remove_all_unavailable):
|
||||
iptv_elem.bind_property("sensitive", self.set_action(h.__name__, h, False), "enabled")
|
||||
|
||||
if self._settings.extensions_support:
|
||||
self.init_extensions(builder)
|
||||
|
||||
def init_extensions(self, builder):
|
||||
import pkgutil
|
||||
# Extensions (Plugins) section.
|
||||
ext_section = builder.get_object(f"{'mac_' if IS_DARWIN else ''}extension_section")
|
||||
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"]
|
||||
extensions = {}
|
||||
|
||||
for importer, name, is_package in pkgutil.iter_modules(ext_paths):
|
||||
if is_package:
|
||||
m = importer.find_module(name).load_module()
|
||||
cls_name = name.capitalize()
|
||||
if hasattr(m, cls_name):
|
||||
cls = getattr(m, cls_name)
|
||||
action_name = f"on_{name}_extension"
|
||||
item = Gio.MenuItem.new(cls.LABEL, f"app.{action_name}")
|
||||
ext_section.append_item(item)
|
||||
extensions[action_name] = cls
|
||||
|
||||
def ac(a, v):
|
||||
c = extensions[a.get_name()]
|
||||
e = c(self)
|
||||
e.exec()
|
||||
|
||||
self.set_action(action_name, ac)
|
||||
|
||||
def init_actions(self):
|
||||
# Main actions.
|
||||
self.set_action("preferences", self.on_settings)
|
||||
self.set_action("about", self.on_about_app)
|
||||
self.set_action("quit", self.on_close_app)
|
||||
# Import.
|
||||
self.set_action("on_import_bouquet", self.on_import_bouquet)
|
||||
self.set_action("on_import_bouquets", self.on_import_bouquets)
|
||||
self.set_action("on_new_configuration", self.on_new_configuration)
|
||||
self.set_action("on_import_from_web", self.on_import_from_web)
|
||||
self.set_action("on_settings", self.on_settings)
|
||||
# Tools.
|
||||
self.set_action("on_backup_tool_show", self.on_backup_tool_show)
|
||||
self.set_action("on_about_app", self.on_about_app)
|
||||
self.set_action("on_close_app", self.on_close_app)
|
||||
self.set_state_action("on_telnet_show", self.on_telnet_show, False)
|
||||
self.set_state_action("on_logs_show", self.on_logs_show, False)
|
||||
# Filter.
|
||||
@@ -731,9 +774,12 @@ class Application(Gtk.Application):
|
||||
# Alternate layout.
|
||||
sa = self.set_state_action("set_alternate_layout", self.set_use_alt_layout, self._settings.alternate_layout)
|
||||
sa.connect("change-state", self.on_layout_change)
|
||||
# Header bar for macOS.
|
||||
sa = self.set_state_action("set_alternate_title", self.set_use_alt_title, self._settings.use_header_bar)
|
||||
sa.set_enabled(IS_DARWIN)
|
||||
# Menu bar and playback.
|
||||
self.set_action("on_playback_close", self._player_box.on_close)
|
||||
if not IS_GNOME_SESSION:
|
||||
if not USE_HEADER_BAR:
|
||||
# We are working with the "hidden-when" submenu attribute. See 'app_menu_.ui' file.
|
||||
hide_bar_action = Gio.SimpleAction.new("hide_menu_bar", None)
|
||||
self._player_box.bind_property("visible", hide_bar_action, "enabled", 4)
|
||||
@@ -767,7 +813,7 @@ class Application(Gtk.Application):
|
||||
self.set_accels_for_action("app.open_data", ["<primary>o"])
|
||||
self.set_accels_for_action("app.on_hide", ["<primary>h"])
|
||||
self.set_accels_for_action("app.on_locked", ["<primary>l"])
|
||||
self.set_accels_for_action("app.on_close_app", ["<primary>q"])
|
||||
self.set_accels_for_action("app.quit", ["<primary>q"])
|
||||
self.set_accels_for_action("app.on_edit", ["<primary>e"])
|
||||
self.set_accels_for_action("app.on_telnet_show", ["<primary>t"])
|
||||
self.set_accels_for_action("app.on_logs_show", ["<shift><primary>l"])
|
||||
@@ -869,12 +915,12 @@ class Application(Gtk.Application):
|
||||
def init_layout(self):
|
||||
""" Initializes an alternate layout, if enabled. """
|
||||
if self._settings.alternate_layout:
|
||||
self._main_paned.pack2(self._player_box, True, False)
|
||||
self._main_paned.pack2(self._player_box, True, True)
|
||||
self.reverse_main_elements(True)
|
||||
else:
|
||||
self._main_paned.remove(self._data_paned)
|
||||
self._main_paned.pack1(self._player_box, True, False)
|
||||
self._main_paned.pack2(self._data_paned, True, False)
|
||||
self._main_paned.pack1(self._player_box, True, True)
|
||||
self._main_paned.pack2(self._data_paned, True, True)
|
||||
|
||||
def init_bq_position(self):
|
||||
self._fav_paned.remove(self._fav_frame)
|
||||
@@ -891,7 +937,7 @@ class Application(Gtk.Application):
|
||||
""" Initializes starting positions of main paned widgets. """
|
||||
width = paned.get_allocated_width()
|
||||
main_position = self._settings.get("data_paned_position", width * 0.5)
|
||||
fav_position = self._settings.get("fav_paned_position", width * 0.27)
|
||||
fav_position = self._settings.get("fav_paned_position", width * 0.25)
|
||||
paned.set_position(main_position)
|
||||
self._fav_paned.set_position(fav_position)
|
||||
|
||||
@@ -1069,6 +1115,17 @@ class Application(Gtk.Application):
|
||||
action.set_state(value)
|
||||
self._settings.alternate_layout = bool(value)
|
||||
|
||||
def set_use_alt_title(self, action, value):
|
||||
action.set_state(value)
|
||||
value = bool(value)
|
||||
self._settings.use_header_bar = bool(value)
|
||||
|
||||
msg = get_message("Restart the program to apply all changes.")
|
||||
if value:
|
||||
warn = "It can cause some problems."
|
||||
msg = f"{get_message('EXPERIMENTAL!')} {warn} {msg}"
|
||||
self.show_info_message(msg, Gtk.MessageType.WARNING)
|
||||
|
||||
@run_idle
|
||||
def on_layout_change(self, action, value):
|
||||
is_alt = bool(value)
|
||||
@@ -1144,10 +1201,16 @@ class Application(Gtk.Application):
|
||||
|
||||
event = self._epg_cache.get_current_event(srv_name)
|
||||
if event:
|
||||
if event.start:
|
||||
start = datetime.fromtimestamp(event.start).strftime(self._epg_start_time_fmt)
|
||||
end = datetime.fromtimestamp(event.end).strftime(self._epg_end_time_fmt)
|
||||
sep = "-"
|
||||
else:
|
||||
start, end, sep = "", "", ""
|
||||
# https://docs.gtk.org/Pango/pango_markup.html
|
||||
renderer.set_property("markup", (f'{escape(srv_name)}\n\n'
|
||||
f'<span size="small" weight="bold">{escape(event.title)}</span>\n'
|
||||
f'<span size="small" style="italic">{event.time}</span>'))
|
||||
f'<span size="small" style="italic">{start} {sep} {end}</span>'))
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -1173,6 +1236,9 @@ class Application(Gtk.Application):
|
||||
self.on_copy(view, target=ViewTarget.BOUQUET)
|
||||
|
||||
def on_copy(self, view, target):
|
||||
if not self._settings.unlimited_copy_buffer:
|
||||
self._bouquets_buffer.clear() if target is ViewTarget.BOUQUET else self._rows_buffer.clear()
|
||||
|
||||
model, paths = view.get_selection().get_selected_rows()
|
||||
|
||||
if target is ViewTarget.FAV:
|
||||
@@ -1201,6 +1267,9 @@ class Application(Gtk.Application):
|
||||
self.on_cut(view, ViewTarget.BOUQUET)
|
||||
|
||||
def on_cut(self, view, target=None):
|
||||
if not self._settings.unlimited_copy_buffer:
|
||||
self._bouquets_buffer.clear() if target is ViewTarget.BOUQUET else self._rows_buffer.clear()
|
||||
|
||||
if target is ViewTarget.FAV:
|
||||
for row in tuple(self.on_delete(view)):
|
||||
self._rows_buffer.append(row)
|
||||
@@ -1268,6 +1337,20 @@ class Application(Gtk.Application):
|
||||
self._bouquets_buffer.clear()
|
||||
self.update_bouquets_type()
|
||||
|
||||
def on_services_update(self, app, services):
|
||||
""" Updates services in the main model. """
|
||||
for r in self._fav_model:
|
||||
fav_id = r[Column.FAV_ID]
|
||||
if fav_id in services:
|
||||
service = services[fav_id]
|
||||
r[Column.FAV_SERVICE] = service.service
|
||||
|
||||
for r in self._services_model:
|
||||
fav_id = r[Column.SRV_FAV_ID]
|
||||
if fav_id in services:
|
||||
service = services[fav_id]
|
||||
r[Column.SRV_SERVICE] = service.service
|
||||
|
||||
# ***************** Deletion ********************* #
|
||||
|
||||
def on_delete(self, view):
|
||||
@@ -1559,7 +1642,7 @@ class Application(Gtk.Application):
|
||||
|
||||
for s_row, row in zip(sorted(map(
|
||||
lambda r: r[:], rows),
|
||||
key=lambda r: r[c_num] or nv if c_num != Column.FAV_POS else self.get_pos_num(r[c_num]),
|
||||
key=lambda r: r[c_num] or nv if c_num != Column.FAV_POS else get_pos_num(r[c_num]),
|
||||
reverse=rev), rows):
|
||||
self._fav_model.set(row.iter, columns, s_row)
|
||||
bq[index] = s_row[Column.FAV_ID]
|
||||
@@ -1575,18 +1658,7 @@ class Application(Gtk.Application):
|
||||
|
||||
def position_sort_func(self, model, iter1, iter2, column):
|
||||
""" Custom sort function for position column. """
|
||||
return self.get_pos_num(model.get_value(iter1, column)) - self.get_pos_num(model.get_value(iter2, column))
|
||||
|
||||
def get_pos_num(self, pos):
|
||||
""" Returns num [float] representation of satellite position. """
|
||||
if not pos:
|
||||
return -183.0
|
||||
|
||||
if len(pos) > 1:
|
||||
m = -1 if pos[-1] == "W" else 1
|
||||
return float(pos[:-1]) * m
|
||||
|
||||
return -181.0 if pos == "T" else -182.0
|
||||
return get_pos_num(model.get_value(iter1, column)) - get_pos_num(model.get_value(iter2, column))
|
||||
|
||||
# ********************* Hints ************************* #
|
||||
|
||||
@@ -2275,7 +2347,13 @@ class Application(Gtk.Application):
|
||||
self.append_bouquet(bq, row.iter)
|
||||
|
||||
def append_bouquet(self, bq, parent):
|
||||
name, bq_type, locked, hidden = bq.name, bq.type, bq.locked, bq.hidden
|
||||
name, bq_type, locked, hidden = bq.name, bq.type, bq.locked, HIDE_ICON if bq.hidden else None
|
||||
# Parental control state.
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
locked = LOCKED_ICON if bq.locked in self._blacklist else None
|
||||
else:
|
||||
locked = LOCKED_ICON if bq.locked else None
|
||||
|
||||
bouquet = self._bouquets_model.append(parent, [name, locked, hidden, bq_type])
|
||||
bq_id = f"{name}:{bq_type}"
|
||||
services = []
|
||||
@@ -2336,7 +2414,10 @@ class Application(Gtk.Application):
|
||||
break
|
||||
|
||||
def append_services(self, services):
|
||||
to_add = []
|
||||
for srv in services:
|
||||
if srv.fav_id not in self._services:
|
||||
to_add.append(srv)
|
||||
# Adding channels to dict with fav_id as keys.
|
||||
self._services[srv.fav_id] = srv
|
||||
self.update_services_counts(len(self._services.values()))
|
||||
@@ -2344,7 +2425,7 @@ class Application(Gtk.Application):
|
||||
self._services_load_spinner.start()
|
||||
factor = self.DEL_FACTOR / 4
|
||||
|
||||
for index, srv in enumerate(services):
|
||||
for index, srv in enumerate(to_add):
|
||||
background = self.get_new_background(srv.flags_cas)
|
||||
s = srv + (None, background)
|
||||
self._services_model.append(s)
|
||||
@@ -2468,7 +2549,7 @@ class Application(Gtk.Application):
|
||||
|
||||
# Getting bouquets
|
||||
self._bouquets_view.get_model().foreach(parse_bouquets)
|
||||
write_bouquets(path, bouquets, profile, self._settings.force_bq_names)
|
||||
write_bouquets(path, bouquets, profile, self._settings.force_bq_names, self._blacklist)
|
||||
yield True
|
||||
# Getting services
|
||||
services_model = get_base_model(self._services_view.get_model())
|
||||
@@ -2717,6 +2798,8 @@ class Application(Gtk.Application):
|
||||
|
||||
if changed:
|
||||
self.open_data()
|
||||
if self._settings.display_epg:
|
||||
self.change_action_state("display_epg", GLib.Variant.new_boolean(self._settings.display_epg))
|
||||
self.emit("profile-changed", None)
|
||||
|
||||
def set_profile(self, active):
|
||||
@@ -2832,9 +2915,6 @@ class Application(Gtk.Application):
|
||||
self._tool_elements[elem].set_sensitive(not_empty)
|
||||
if elem == "bouquets_paste_popup_item":
|
||||
self._tool_elements[elem].set_sensitive(not_empty and self._bouquets_buffer)
|
||||
if self._s_type is SettingsType.NEUTRINO_MP:
|
||||
for elem in self._LOCK_HIDE_ELEMENTS:
|
||||
self._tool_elements[elem].set_sensitive(not_empty)
|
||||
else:
|
||||
for elem in self._FAV_ELEMENTS:
|
||||
if elem in ("paste_tool_button", "fav_paste_popup_item"):
|
||||
@@ -2847,8 +2927,6 @@ class Application(Gtk.Application):
|
||||
self._tool_elements[elem].set_sensitive(not_empty and is_service)
|
||||
for elem in self._BOUQUET_ELEMENTS:
|
||||
self._tool_elements[elem].set_sensitive(False)
|
||||
for elem in self._LOCK_HIDE_ELEMENTS:
|
||||
self._tool_elements[elem].set_sensitive(not_empty and self._s_type is SettingsType.ENIGMA_2)
|
||||
|
||||
for elem in self._FAV_IPTV_ELEMENTS:
|
||||
is_iptv = self._bq_selected and not is_service
|
||||
@@ -2869,14 +2947,21 @@ class Application(Gtk.Application):
|
||||
self.set_service_flags(Flag.LOCK)
|
||||
|
||||
def set_service_flags(self, flag):
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
set_flags(flag, self._services_view, self._fav_view, self._services, self._blacklist)
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP and self._bq_selected:
|
||||
if self._bouquets_view.is_focus() and self._bq_selected:
|
||||
model, paths = self._bouquets_view.get_selection().get_selected_rows()
|
||||
itr = model.get_iter(paths[0])
|
||||
value = model.get_value(itr, 1 if flag is Flag.LOCK else 2)
|
||||
value = None if value else LOCKED_ICON if flag is Flag.LOCK else HIDE_ICON
|
||||
model.set_value(itr, 1 if flag is Flag.LOCK else 2, value)
|
||||
for p in paths:
|
||||
itr = model.get_iter(p)
|
||||
if not model.iter_has_child(itr):
|
||||
value = model.get_value(itr, 1 if flag is Flag.LOCK else 2)
|
||||
value = None if value else LOCKED_ICON if flag is Flag.LOCK else HIDE_ICON
|
||||
model.set_value(itr, 1 if flag is Flag.LOCK else 2, value)
|
||||
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
msg = get_message("After uploading the changes you may need to completely reboot the receiver!")
|
||||
self.show_info_message(f"{get_message('EXPERIMENTAL!')} {msg}", Gtk.MessageType.WARNING)
|
||||
else:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
set_flags(flag, self._services_view, self._fav_view, self._services, self._blacklist)
|
||||
|
||||
def on_model_changed(self, model, path=None, itr=None):
|
||||
model_name = model.get_name()
|
||||
@@ -3166,23 +3251,23 @@ class Application(Gtk.Application):
|
||||
ServicesUpdateDialog(self._main_window, self._settings, self.on_import_data_from_web).show()
|
||||
|
||||
@run_idle
|
||||
def on_import_data_from_web(self, services):
|
||||
def on_import_data_from_web(self, services, bouquets=None):
|
||||
msg = "Combine with the current data?"
|
||||
|
||||
def clb():
|
||||
self.show_info_message("Done!")
|
||||
|
||||
if len(self._services_model) > 0 and show_dialog(DialogType.QUESTION, self._main_window,
|
||||
msg) == Gtk.ResponseType.OK:
|
||||
gen = self.append_imported_data([], services)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
gen = self.append_imported_data(bouquets or [], services, clb)
|
||||
else:
|
||||
gen = self.import_data_from_web(services)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
gen = self.import_data_from_web(services, bouquets, clb)
|
||||
GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW)
|
||||
|
||||
def import_data_from_web(self, services):
|
||||
def import_data_from_web(self, services, bouquets, callback=None):
|
||||
self._wait_dialog.show()
|
||||
if self._app_info_box.get_visible():
|
||||
yield from self.create_new_configuration(self._s_type)
|
||||
yield from self.append_services(services)
|
||||
self.update_sat_positions()
|
||||
yield True
|
||||
yield from self.create_new_configuration(self._s_type)
|
||||
yield from self.append_imported_data(bouquets or [], services, callback)
|
||||
self._wait_dialog.hide()
|
||||
|
||||
# ***************** Export ******************** #
|
||||
@@ -3307,7 +3392,7 @@ class Application(Gtk.Application):
|
||||
def on_playback_full_screen(self, box, state):
|
||||
self._data_paned.set_visible(state)
|
||||
self._main_window.unfullscreen() if state else self._main_window.fullscreen()
|
||||
if not IS_GNOME_SESSION:
|
||||
if not USE_HEADER_BAR:
|
||||
self._main_window.set_show_menubar(state)
|
||||
|
||||
def on_playback_show(self, box):
|
||||
@@ -3500,7 +3585,7 @@ class Application(Gtk.Application):
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
# It may require some correction for cable and terrestrial channels!
|
||||
try:
|
||||
pos, freq = int(self.get_pos_num(srv.pos)) * 10, int(srv.freq)
|
||||
pos, freq = int(get_pos_num(srv.pos)) * 10, int(srv.freq)
|
||||
tid, nid, sid = int(ref[: -8], 16), int(ref[-8: -4], 16), int(srv.ssid, 16)
|
||||
except ValueError:
|
||||
log(f"Error getting reference for: {srv}")
|
||||
@@ -3702,16 +3787,7 @@ class Application(Gtk.Application):
|
||||
elif self._s_type is SettingsType.NEUTRINO_MP:
|
||||
list(map(lambda s: self._sat_positions.add(s.pos), filter(lambda s: s.pos, self._services.values())))
|
||||
|
||||
self.update_filter_sat_positions()
|
||||
|
||||
def update_filter_sat_positions(self):
|
||||
""" Updates the values for the satellite positions button model. """
|
||||
first = self._filter_sat_pos_model[self._filter_sat_pos_model.get_iter_first()][:]
|
||||
self._filter_sat_pos_model.clear()
|
||||
self._filter_sat_pos_model.append((first[0], True))
|
||||
self._sat_positions.discard(first[0])
|
||||
list(map(lambda pos: self._filter_sat_pos_model.append((pos, True)),
|
||||
sorted(self._sat_positions, key=self.get_pos_num, reverse=True)))
|
||||
update_filter_sat_positions(self._filter_sat_pos_model, self._sat_positions)
|
||||
|
||||
@run_with_delay(2)
|
||||
def on_filter_changed(self, item=None):
|
||||
@@ -3794,16 +3870,7 @@ class Application(Gtk.Application):
|
||||
self.on_filter_changed()
|
||||
|
||||
def update_filter_toggle_model(self, model, toggle, path, values_set):
|
||||
active = not toggle.get_active()
|
||||
if path == "0":
|
||||
model.foreach(lambda m, p, i: m.set_value(i, 1, active))
|
||||
else:
|
||||
model.set_value(model.get_iter(path), 1, active)
|
||||
if active:
|
||||
model.set_value(model.get_iter_first(), 1, len({r[0] for r in model if r[1]}) == len(model) - 1)
|
||||
else:
|
||||
model.set_value(model.get_iter_first(), 1, False)
|
||||
|
||||
update_toggle_model(model, path, toggle)
|
||||
values_set.clear()
|
||||
values_set.update({r[0] for r in model if r[1]})
|
||||
self.on_iptv_filter_changed() if self._iptv_button.get_active() else self.on_filter_changed()
|
||||
@@ -3960,6 +4027,26 @@ class Application(Gtk.Application):
|
||||
if r[Column.FAV_SERVICE] in dup:
|
||||
r[Column.FAV_BACKGROUND] = self._NEW_COLOR
|
||||
|
||||
def on_remove_duplicates(self, item):
|
||||
exist = set()
|
||||
to_remove = []
|
||||
for r in self._fav_model:
|
||||
fav_id = r[Column.FAV_ID]
|
||||
if fav_id in exist:
|
||||
to_remove.append(r.iter)
|
||||
else:
|
||||
exist.add(fav_id)
|
||||
|
||||
count = len(to_remove)
|
||||
if count:
|
||||
if show_dialog(DialogType.QUESTION, self._main_window) != Gtk.ResponseType.OK:
|
||||
return
|
||||
gen = self.remove_favs(to_remove, self._fav_model)
|
||||
GLib.idle_add(lambda: next(gen, False))
|
||||
self.show_info_message(f"{get_message('Done!')} {get_message('Removed')}: {count}")
|
||||
else:
|
||||
self.show_info_message(f"{get_message('Done!')} {get_message('Found')}: {count}")
|
||||
|
||||
def on_services_mark_not_in_bouquets(self, item):
|
||||
if self.is_data_loading():
|
||||
self.show_error_message("Data loading in progress!")
|
||||
@@ -4295,11 +4382,15 @@ class Application(Gtk.Application):
|
||||
return True
|
||||
|
||||
def on_alt_selection(self, model, path, column):
|
||||
if self._control_tool and self._control_tool.update_epg:
|
||||
if self._page is Page.EPG:
|
||||
row = model[path][:]
|
||||
srv = self._services.get(row[Column.ALT_FAV_ID], None)
|
||||
if srv and srv.transponder or row[Column.ALT_TYPE] == BqServiceType.IPTV.name:
|
||||
self._control_tool.on_service_changed(srv.picon_id.rstrip(".png").replace("_", ":"))
|
||||
ref = self.get_service_ref_data(srv)
|
||||
if not ref:
|
||||
return
|
||||
|
||||
self.emit("fav-changed", ref)
|
||||
|
||||
# ***************** Profile label ********************* #
|
||||
|
||||
@@ -4324,7 +4415,7 @@ class Application(Gtk.Application):
|
||||
self.show_info_message(message, Gtk.MessageType.ERROR)
|
||||
|
||||
@run_idle
|
||||
def show_info_message(self, text, message_type):
|
||||
def show_info_message(self, text, message_type=Gtk.MessageType.INFO):
|
||||
self._info_bar.set_visible(False)
|
||||
self._info_label.set_text(get_message(text))
|
||||
self._info_bar.set_message_type(message_type)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -32,7 +32,8 @@ __all__ = ("insert_marker", "move_items", "rename", "ViewTarget", "set_flags", "
|
||||
"scroll_to", "get_base_model", "get_base_paths", "copy_reference", "assign_picons", "remove_picon",
|
||||
"is_only_one_item_selected", "gen_bouquets", "BqGenType", "get_selection", "get_service_reference",
|
||||
"get_model_data", "remove_all_unused_picons", "get_picon_pixbuf", "get_base_itrs", "get_iptv_url",
|
||||
"get_iptv_data", "update_entry_data", "append_text_to_tview", "on_popup_menu", "get_picon_file_name")
|
||||
"get_iptv_data", "update_entry_data", "append_text_to_tview", "on_popup_menu", "get_picon_file_name",
|
||||
"update_toggle_model", "update_popup_filter_model", "update_filter_sat_positions", "get_pos_num")
|
||||
|
||||
import os
|
||||
import re
|
||||
@@ -609,8 +610,11 @@ def gen_bouquets(view, bq_view, transient, gen_type, s_type, callback):
|
||||
callback(Bouquet(cond, bq_type, s_data.get(cond)), bq_root_iter)
|
||||
else:
|
||||
# We add a bouquet only if the given name is missing [keys - names]!
|
||||
for name in sorted(s_data.keys() - bq_names):
|
||||
callback(Bouquet(name, BqType.TV.value, s_data.get(name)), bq_root_iter)
|
||||
if gen_type is BqGenType.EACH_SAT:
|
||||
bq_names = sorted(s_data.keys() - bq_names, key=get_pos_num, reverse=True)
|
||||
else:
|
||||
bq_names = sorted(s_data.keys() - bq_names)
|
||||
[callback(Bouquet(name, BqType.TV.value, s_data.get(name)), bq_root_iter) for name in bq_names]
|
||||
|
||||
|
||||
def get_bouquets_names(model):
|
||||
@@ -697,6 +701,44 @@ def get_model_data(view):
|
||||
return model_name, model
|
||||
|
||||
|
||||
def update_toggle_model(model, path, toggle):
|
||||
""" Updates the toggle state for the model. """
|
||||
active = not toggle.get_active()
|
||||
if path == "0":
|
||||
model.foreach(lambda m, p, i: m.set_value(i, 1, active))
|
||||
else:
|
||||
model.set_value(model.get_iter(path), 1, active)
|
||||
if active:
|
||||
model.set_value(model.get_iter_first(), 1, len({r[0] for r in model if r[1]}) == len(model) - 1)
|
||||
else:
|
||||
model.set_value(model.get_iter_first(), 1, False)
|
||||
|
||||
|
||||
def update_popup_filter_model(model, elements: set):
|
||||
first = model[model.get_iter_first()][:]
|
||||
model.clear()
|
||||
model.append((first[0], True))
|
||||
elements.discard(first[0])
|
||||
|
||||
|
||||
def update_filter_sat_positions(model, sat_positions):
|
||||
""" Updates the values for the satellite positions button model. """
|
||||
update_popup_filter_model(model, sat_positions)
|
||||
list(map(lambda pos: model.append((pos, True)), sorted(sat_positions, key=get_pos_num, reverse=True)))
|
||||
|
||||
|
||||
def get_pos_num(pos):
|
||||
""" Returns num [float] representation of satellite position. """
|
||||
if not pos:
|
||||
return -183.0
|
||||
|
||||
if len(pos) > 1:
|
||||
m = -1 if pos[-1] == "W" else 1
|
||||
return float(pos[:-1]) * m
|
||||
|
||||
return -181.0 if pos == "T" else -182.0
|
||||
|
||||
|
||||
def append_text_to_tview(char, view):
|
||||
""" Appending text and scrolling to a given line in the text view. """
|
||||
buf = view.get_buffer()
|
||||
@@ -720,7 +762,7 @@ def get_iptv_data(fav_id):
|
||||
data, sep, desc = fav_id.partition("#DESCRIPTION")
|
||||
data = data.split(":")
|
||||
if len(data) < 11:
|
||||
return None, None, desc
|
||||
return None, desc
|
||||
return ":".join(data[:10]), unquote(data[10].strip())
|
||||
|
||||
|
||||
|
||||
@@ -641,7 +641,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Drag the services to the desired picon or picon to the list of selected services.</property>
|
||||
<property name="model">picons_src_sort_model</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">horizontal</property>
|
||||
<property name="tooltip_column">0</property>
|
||||
@@ -666,7 +665,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="picons_src_renderer">
|
||||
<property name="height">50</property>
|
||||
<property name="ypad">5</property>
|
||||
</object>
|
||||
<attributes>
|
||||
@@ -779,7 +777,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="can_focus">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Drag the services to the desired picon or picon to the list of selected services.</property>
|
||||
<property name="model">picons_dst_sort_model</property>
|
||||
<property name="fixed_height_mode">True</property>
|
||||
<property name="rubber_banding">True</property>
|
||||
<property name="enable_grid_lines">horizontal</property>
|
||||
<property name="tooltip_column">0</property>
|
||||
@@ -805,7 +802,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="alignment">0.49000000953674316</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="picons_dest_renderer">
|
||||
<property name="height">50</property>
|
||||
<property name="ypad">5</property>
|
||||
</object>
|
||||
<attributes>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -34,17 +34,17 @@ from gi.repository import GLib, GObject, Gio
|
||||
from app.commons import run_idle, run_with_delay
|
||||
from app.connections import HttpAPI
|
||||
from app.eparser.ecommons import BqServiceType
|
||||
from app.settings import PlayStreamsMode, IS_DARWIN, SettingsType
|
||||
from app.settings import PlayStreamsMode, IS_DARWIN, SettingsType, USE_HEADER_BAR
|
||||
from app.tools.media import Player
|
||||
from app.ui.dialogs import get_builder, get_message
|
||||
from app.ui.main_helper import get_iptv_url
|
||||
from app.ui.uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, Column, IS_GNOME_SESSION, Page
|
||||
from app.ui.uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, Column, Page
|
||||
|
||||
|
||||
class PlayerBox(Gtk.Box):
|
||||
|
||||
def __init__(self, app, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
def __init__(self, app, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
# Signals.
|
||||
GObject.signal_new("playback-full-screen", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
@@ -54,6 +54,8 @@ class PlayerBox(Gtk.Box):
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("stop", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
GObject.signal_new("pause", self, GObject.SIGNAL_RUN_LAST,
|
||||
GObject.TYPE_PYOBJECT, (GObject.TYPE_PYOBJECT,))
|
||||
|
||||
self._app = app
|
||||
self._app.connect("fav-clicked", self.on_fav_clicked)
|
||||
@@ -106,6 +108,9 @@ class PlayerBox(Gtk.Box):
|
||||
if mode is not FavClickMode.STREAM and not self._app.http_api:
|
||||
return
|
||||
|
||||
if len(self._fav_view.get_model()) == 0:
|
||||
return
|
||||
|
||||
self._fav_view.set_sensitive(False)
|
||||
if mode is FavClickMode.STREAM:
|
||||
self.on_play_stream()
|
||||
@@ -189,7 +194,7 @@ class PlayerBox(Gtk.Box):
|
||||
video_menu = builder.get_object("video_menu")
|
||||
subtitle_menu = builder.get_object("subtitle_menu")
|
||||
|
||||
if not IS_GNOME_SESSION:
|
||||
if not USE_HEADER_BAR:
|
||||
menu_bar = self._app.get_menubar()
|
||||
menu_bar.insert_section(1, None, audio_menu)
|
||||
menu_bar.insert_section(2, None, video_menu)
|
||||
@@ -284,7 +289,9 @@ class PlayerBox(Gtk.Box):
|
||||
|
||||
def on_press(self, area, event):
|
||||
if event.button == Gdk.BUTTON_PRIMARY:
|
||||
if event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
||||
if event.type == Gdk.EventType.BUTTON_PRESS:
|
||||
self.emit("pause", None)
|
||||
elif event.type == Gdk.EventType.DOUBLE_BUTTON_PRESS:
|
||||
self.on_full_screen()
|
||||
|
||||
def on_key_press(self, widget, event):
|
||||
|
||||
@@ -79,7 +79,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="image">set_default_image</property>
|
||||
<property name="use-stock">False</property>
|
||||
<signal name="activate" handler="on_profile_set_default" swapped="no"/>
|
||||
<accelerator key="d" signal="activate"/>
|
||||
<accelerator key="d" signal="activate" modifiers="Primary"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -291,7 +291,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Save</property>
|
||||
<property name="valign">center</property>
|
||||
<accelerator key="s" signal="clicked"/>
|
||||
<accelerator key="s" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -508,7 +508,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="icon-name">emblem-default</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="d" signal="clicked"/>
|
||||
<accelerator key="d" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1671,47 +1671,137 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="load_on_startup_frame">
|
||||
<object class="GtkFrame" id="program_options_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="load_on_startup_box">
|
||||
<object class="GtkBox" id="program_options_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="load_on_startup_label">
|
||||
<object class="GtkBox" id="load_on_startup_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes">Load the last open configuration at program startup</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="load_on_startup_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label" translatable="yes">Load the last open configuration at program startup</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="load_on_startup_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="load_on_startup_switch">
|
||||
<object class="GtkBox" id="enable_extensions_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">EXPERIMENTAL!</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_extensions_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable extensions support</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="enable_extensions_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_http_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_http_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable HTTP API</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="support_http_api_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<signal name="state-set" handler="on_http_mode_switch" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
@@ -1882,59 +1972,6 @@ Author: Dmitriy Yefremov
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="http_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.019999999552965164</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_http_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enable_http_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable HTTP API</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="support_http_api_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
<signal name="state-set" handler="on_http_mode_switch" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="name">program</property>
|
||||
@@ -2737,10 +2774,7 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkGrid" id="bq_naming_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="border-width">5</property>
|
||||
<property name="row-spacing">5</property>
|
||||
<property name="column-spacing">5</property>
|
||||
<child>
|
||||
@@ -2950,16 +2984,13 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkBox" id="experimental_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-start">5</property>
|
||||
<property name="margin-end">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="border-width">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="v5_support_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="sensitive" bind-source="enable_experimental_switch" bind-property="active">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
@@ -2989,6 +3020,46 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="unlimited_buffer_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive" bind-source="enable_experimental_switch" bind-property="active">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Enables unlimited copy buffer for the bouquets tab.</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="unlimited_buffer_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="label" translatable="yes">Enable unlimited copy buffer</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch" id="unlimited_buffer_switch">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="halign">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
@@ -2998,7 +3069,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="yt_dl_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="sensitive" bind-source="enable_experimental_switch" bind-property="active">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">10</property>
|
||||
<property name="margin-bottom">10</property>
|
||||
@@ -3064,7 +3135,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="yt_dl_update_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="sensitive" bind-source="enable_yt_dl_switch" bind-property="active">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="auto_update_yt_dl_label">
|
||||
@@ -3104,13 +3175,13 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="enable_direct_playback_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="sensitive" bind-source="enable_experimental_switch" bind-property="active">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Enables direct sending and playback of media links on the receiver</property>
|
||||
<child>
|
||||
@@ -3144,7 +3215,7 @@ Author: Dmitriy Yefremov
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -35,7 +35,7 @@ from app.connections import test_telnet, test_ftp, TestException, test_http, Htt
|
||||
from app.settings import SettingsType, Settings, PlayStreamsMode, IS_LINUX, SEP, IS_WIN
|
||||
from app.ui.dialogs import show_dialog, DialogType, get_message, get_chooser_dialog, get_builder
|
||||
from .main_helper import update_entry_data, scroll_to, get_picon_pixbuf
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT, IS_GNOME_SESSION
|
||||
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT, HeaderBar
|
||||
|
||||
|
||||
class SettingsDialog:
|
||||
@@ -180,16 +180,13 @@ class SettingsDialog:
|
||||
self._compress_picons_switch = builder.get_object("compress_picons_switch")
|
||||
self._force_bq_name_switch = builder.get_object("force_bq_name_switch")
|
||||
self._support_ver5_switch = builder.get_object("support_ver5_switch")
|
||||
self._unlimited_buffer_switch = builder.get_object("unlimited_buffer_switch")
|
||||
self._enable_extensions_switch = builder.get_object("enable_extensions_switch")
|
||||
self._support_http_api_switch = builder.get_object("support_http_api_switch")
|
||||
self._enable_yt_dl_switch = builder.get_object("enable_yt_dl_switch")
|
||||
self._enable_update_yt_dl_switch = builder.get_object("enable_update_yt_dl_switch")
|
||||
self._enable_send_to_switch = builder.get_object("enable_send_to_switch")
|
||||
# EXPERIMENTAL.
|
||||
self._enable_exp_switch = builder.get_object("enable_experimental_switch")
|
||||
self._enable_exp_switch.bind_property("active", builder.get_object("yt_dl_box"), "sensitive")
|
||||
self._enable_yt_dl_switch.bind_property("active", builder.get_object("yt_dl_update_box"), "sensitive")
|
||||
self._enable_exp_switch.bind_property("active", builder.get_object("v5_support_box"), "sensitive")
|
||||
self._enable_exp_switch.bind_property("active", builder.get_object("enable_direct_playback_box"), "sensitive")
|
||||
# Enigma2 only.
|
||||
self._enigma_radio_button.bind_property("active", builder.get_object("bq_naming_grid"), "sensitive")
|
||||
self._enigma_radio_button.bind_property("active", builder.get_object("program_frame"), "sensitive")
|
||||
@@ -211,13 +208,14 @@ class SettingsDialog:
|
||||
[self.init_element_style(el, screen, style_provider) for el in self._digit_elems]
|
||||
self.init_element_style(self._host_field, screen, style_provider)
|
||||
|
||||
if IS_GNOME_SESSION:
|
||||
if self._settings.use_header_bar:
|
||||
switcher = builder.get_object("main_stack_switcher")
|
||||
switcher.set_margin_top(0)
|
||||
switcher.set_margin_bottom(0)
|
||||
builder.get_object("main_box").remove(switcher)
|
||||
header_bar = Gtk.HeaderBar(visible=True, show_close_button=True)
|
||||
header_bar = HeaderBar()
|
||||
header_bar.set_custom_title(switcher)
|
||||
|
||||
self._dialog.set_titlebar(header_bar)
|
||||
|
||||
self.init_ui_elements()
|
||||
@@ -347,6 +345,8 @@ class SettingsDialog:
|
||||
if self._s_type is SettingsType.ENIGMA_2:
|
||||
self._enable_exp_switch.set_active(self._settings.is_enable_experimental)
|
||||
self._support_ver5_switch.set_active(self._settings.v5_support)
|
||||
self._unlimited_buffer_switch.set_active(self._settings.unlimited_copy_buffer)
|
||||
self._enable_extensions_switch.set_active(self._settings.extensions_support)
|
||||
self._use_http_switch.set_active(self._settings.use_http)
|
||||
self._remove_unused_bq_switch.set_active(self._settings.remove_unused_bouquets)
|
||||
self._keep_power_mode_switch.set_active(self._settings.keep_power_mode)
|
||||
@@ -430,6 +430,8 @@ class SettingsDialog:
|
||||
self._ext_settings.new_color = self._new_color_button.get_rgba().to_string()
|
||||
self._ext_settings.extra_color = self._extra_color_button.get_rgba().to_string()
|
||||
self._ext_settings.v5_support = self._support_ver5_switch.get_active()
|
||||
self._ext_settings.unlimited_copy_buffer = self._unlimited_buffer_switch.get_active()
|
||||
self._ext_settings.extensions_support = self._enable_extensions_switch.get_active()
|
||||
self._ext_settings.use_http = self._use_http_switch.get_active()
|
||||
self._ext_settings.remove_unused_bouquets = self._remove_unused_bq_switch.get_active()
|
||||
self._ext_settings.keep_power_mode = self._keep_power_mode_switch.get_active()
|
||||
@@ -516,6 +518,7 @@ class SettingsDialog:
|
||||
def on_experimental_switch(self, switch, state):
|
||||
if not state:
|
||||
self._support_ver5_switch.set_active(state)
|
||||
self._unlimited_buffer_switch.set_active(state)
|
||||
self._enable_send_to_switch.set_active(state)
|
||||
self._enable_yt_dl_switch.set_active(state)
|
||||
|
||||
@@ -645,11 +648,13 @@ class SettingsDialog:
|
||||
if response is Gtk.ResponseType.CANCEL:
|
||||
return
|
||||
|
||||
if response in self._settings.picons_paths:
|
||||
sep = "/"
|
||||
path = response if response.endswith(sep) else response + sep
|
||||
|
||||
if path in self._settings.picons_paths:
|
||||
self.show_info_message("This path already exists!", Gtk.MessageType.ERROR)
|
||||
return
|
||||
|
||||
path = response if response.endswith(SEP) else response + SEP
|
||||
model = self._picons_paths_box.get_model()
|
||||
model.append((path, path))
|
||||
self._picons_paths_box.set_active_id(path)
|
||||
|
||||
@@ -31,9 +31,10 @@ from datetime import datetime, timedelta
|
||||
from enum import Enum
|
||||
from urllib.parse import quote
|
||||
|
||||
from app.settings import USE_HEADER_BAR
|
||||
from app.ui.main_helper import on_popup_menu
|
||||
from .dialogs import get_builder, get_message, show_dialog, DialogType
|
||||
from .uicommons import Gtk, Gdk, GLib, UI_RESOURCES_PATH, Page, Column, KeyboardKey, IS_GNOME_SESSION, MOD_MASK
|
||||
from .uicommons import Gtk, Gdk, GLib, UI_RESOURCES_PATH, Page, Column, KeyboardKey, MOD_MASK
|
||||
from ..commons import run_idle, log
|
||||
from ..connections import HttpAPI
|
||||
from ..eparser.ecommons import BqServiceType
|
||||
@@ -56,7 +57,7 @@ class TimerTool(Gtk.Box):
|
||||
|
||||
class TimerDialog(Gtk.Dialog):
|
||||
def __init__(self, parent, action=None, timer_data=None, *args, **kwargs):
|
||||
super().__init__(use_header_bar=IS_GNOME_SESSION, *args, **kwargs)
|
||||
super().__init__(use_header_bar=USE_HEADER_BAR, *args, **kwargs)
|
||||
|
||||
self._action = action or TimerTool.TimerAction.ADD
|
||||
self._timer_data = timer_data or {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -52,7 +52,6 @@ TEXT_DOMAIN = "demon-editor"
|
||||
|
||||
NOTIFY_IS_INIT = False
|
||||
APP_FONT = None
|
||||
IS_GNOME_SESSION = int(bool(os.environ.get("GNOME_DESKTOP_SESSION_ID")))
|
||||
|
||||
try:
|
||||
settings = Settings.get_instance()
|
||||
@@ -162,6 +161,18 @@ def show_notification(message, timeout=10000, urgency=1):
|
||||
notify.show()
|
||||
|
||||
|
||||
class HeaderBar(Gtk.HeaderBar):
|
||||
""" Custom header bar widget. """
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.set_visible(True)
|
||||
self.set_show_close_button(True)
|
||||
|
||||
if IS_DARWIN:
|
||||
self.set_decoration_layout("close,minimize,maximize")
|
||||
|
||||
|
||||
class Page(Enum):
|
||||
""" Main stack widget page. """
|
||||
INFO = "info"
|
||||
@@ -269,6 +280,14 @@ class Column(IntEnum):
|
||||
IPTV_FAV_ID = 5
|
||||
IPTV_PICON_ID = 6
|
||||
IPTV_TOOLTIP = 7
|
||||
# EPG view
|
||||
EPG_SERVICE = 0
|
||||
EPG_TITLE = 1
|
||||
EPG_START = 2
|
||||
EPG_END = 3
|
||||
EPG_LENGTH = 4
|
||||
EPG_DESC = 5
|
||||
EPG_DATA = 6
|
||||
|
||||
def __index__(self):
|
||||
""" Overridden to get the index in slices directly """
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (c) 2018-2023 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
|
||||
@@ -30,6 +30,8 @@ import concurrent.futures
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
from itertools import groupby
|
||||
from math import fabs
|
||||
|
||||
from gi.repository import GLib
|
||||
@@ -38,12 +40,14 @@ from app.commons import run_idle, run_task, log
|
||||
from app.eparser import Satellite, Transponder
|
||||
from app.eparser.ecommons import (PLS_MODE, get_key_by_value, POLARIZATION, FEC, SYSTEM, MODULATION, Terrestrial, Cable,
|
||||
T_SYSTEM, BANDWIDTH, CONSTELLATION, T_FEC, GUARD_INTERVAL, TRANSMISSION_MODE,
|
||||
HIERARCHY, Inversion, C_MODULATION, FEC_DEFAULT, TerTransponder, CableTransponder)
|
||||
HIERARCHY, Inversion, C_MODULATION, FEC_DEFAULT, TerTransponder, CableTransponder,
|
||||
Bouquet, BouquetService, BqServiceType, Bouquets, BqType)
|
||||
from app.settings import USE_HEADER_BAR
|
||||
from app.tools.satellites import SatellitesParser, SatelliteSource, ServicesParser
|
||||
from ..dialogs import show_dialog, DialogType, get_message, get_builder
|
||||
from ..main_helper import append_text_to_tview, get_base_model, on_popup_menu
|
||||
from ..search import SearchProvider
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, IS_GNOME_SESSION
|
||||
from ..uicommons import Gtk, Gdk, UI_RESOURCES_PATH, HeaderBar
|
||||
|
||||
_DIALOGS_UI_PATH = f"{UI_RESOURCES_PATH}xml{os.sep}dialogs.glade"
|
||||
|
||||
@@ -60,7 +64,7 @@ class DVBDialog(Gtk.Dialog):
|
||||
skip_taskbar_hint=True,
|
||||
skip_pager_hint=True,
|
||||
destroy_with_parent=True,
|
||||
use_header_bar=IS_GNOME_SESSION,
|
||||
use_header_bar=USE_HEADER_BAR,
|
||||
window_position=Gtk.WindowPosition.CENTER_ON_PARENT,
|
||||
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK),
|
||||
*args, **kwargs)
|
||||
@@ -432,9 +436,10 @@ class UpdateDialog:
|
||||
update_button = builder.get_object("sat_update_button")
|
||||
self._sat_view.bind_property("sensitive", update_button, "sensitive")
|
||||
self._sat_view.bind_property("sensitive", self._source_box, "sensitive")
|
||||
self._sat_view.bind_property("sensitive", self._source_box, "sensitive")
|
||||
self._sat_view.bind_property("sensitive", self._receive_button, "sensitive")
|
||||
self._receive_button.bind_property("visible", update_button, "visible")
|
||||
self._left_action_box = builder.get_object("sat_update_left_action_box")
|
||||
self._right_action_box = builder.get_object("sat_update_right_action_box")
|
||||
# Filter
|
||||
self._filter_bar = builder.get_object("sat_update_filter_bar")
|
||||
self._from_pos_button = builder.get_object("from_pos_button")
|
||||
@@ -456,6 +461,20 @@ class UpdateDialog:
|
||||
builder.get_object("sat_update_search_down_button"),
|
||||
builder.get_object("sat_update_search_up_button"))
|
||||
builder.get_object("sat_update_find_button").connect("toggled", search_provider.on_search_toggled)
|
||||
# Satellite lists init on dialog start.
|
||||
self._sat_view.connect("realize", self.on_update_satellites_list)
|
||||
|
||||
if self._settings.use_header_bar:
|
||||
header_bar = HeaderBar()
|
||||
builder.get_object("sat_update_header").set_visible(False)
|
||||
header_box = builder.get_object("satellites_update_header_box")
|
||||
header_box.remove(self._source_box)
|
||||
header_bar.pack_start(self._source_box)
|
||||
header_box.remove(self._left_action_box)
|
||||
header_bar.pack_start(self._left_action_box)
|
||||
header_box.remove(self._right_action_box)
|
||||
header_bar.pack_end(self._right_action_box)
|
||||
self._window.set_titlebar(header_bar)
|
||||
|
||||
window_size = self._settings.get(self._size_name)
|
||||
if window_size:
|
||||
@@ -483,11 +502,11 @@ class UpdateDialog:
|
||||
|
||||
self.is_download = True
|
||||
self._sat_view.set_sensitive(False)
|
||||
src = self._source_box.get_active()
|
||||
|
||||
if not self._parser:
|
||||
self._parser = SatellitesParser()
|
||||
|
||||
self.get_sat_list(src, self.append_satellites)
|
||||
self.get_sat_list(self._source_box.get_active(), self.append_satellites)
|
||||
|
||||
def clear_data(self):
|
||||
get_base_model(self._sat_view.get_model()).clear()
|
||||
@@ -517,6 +536,7 @@ class UpdateDialog:
|
||||
|
||||
self._sat_view.set_sensitive(True)
|
||||
self._satellites_count_label.set_text(str(len(model)))
|
||||
self.update_receive_button_state(self._filter_model)
|
||||
|
||||
@run_idle
|
||||
def on_receive_data(self, item):
|
||||
@@ -696,7 +716,7 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
self._services = {}
|
||||
self._selected_transponders = set()
|
||||
self._services_parser = ServicesParser(source=SatelliteSource.LYNGSAT)
|
||||
# Transponder view popup menu
|
||||
# Transponder view popup menu.
|
||||
tr_popup_menu = Gtk.Menu()
|
||||
select_all_item = Gtk.ImageMenuItem.new_from_stock("gtk-select-all")
|
||||
select_all_item.connect("activate", lambda w: self.update_transponder_selection(True))
|
||||
@@ -714,6 +734,41 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
|
||||
self._transponder_paned.set_visible(True)
|
||||
self._source_box.connect("changed", self.on_update_satellites_list)
|
||||
self._source_box.connect("changed", self.on_source_changed)
|
||||
# Options for KingOfSat source.
|
||||
popover = Gtk.Popover()
|
||||
main_options_box = Gtk.Box(spacing=5, margin_left=10, margin_right=10, orientation=Gtk.Orientation.VERTICAL)
|
||||
self._kos_bq_groups_switch = Gtk.Switch()
|
||||
self._kos_bq_lang_switch = Gtk.Switch()
|
||||
self._kos_options_box = Gtk.Box(spacing=5, orientation=Gtk.Orientation.VERTICAL)
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5, margin_top=5)
|
||||
box.pack_start(Gtk.Label(get_message("Create Category bouquets")), False, True, 0)
|
||||
box.pack_end(self._kos_bq_groups_switch, False, True, 0)
|
||||
self._kos_options_box.add(box)
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5, margin_bottom=5)
|
||||
box.pack_start(Gtk.Label(get_message("Create Regional bouquets")), False, True, 0)
|
||||
box.pack_end(self._kos_bq_lang_switch, False, True, 0)
|
||||
self._kos_options_box.add(box)
|
||||
main_options_box.add(self._kos_options_box)
|
||||
# General options.
|
||||
self._general_options_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
self._skip_c_band_switch = Gtk.Switch()
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5, margin_top=5)
|
||||
box.pack_start(Gtk.Label(get_message("Skip C-band")), False, True, 0)
|
||||
box.pack_end(self._skip_c_band_switch, False, True, 0)
|
||||
self._general_options_box.add(box)
|
||||
main_options_box.add(self._general_options_box)
|
||||
main_options_box.add(Gtk.ModelButton(get_message("Close"), margin_bottom=5))
|
||||
main_options_box.show_all()
|
||||
popover.add(main_options_box)
|
||||
# Options button.
|
||||
option_button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5)
|
||||
option_button_box.add(Gtk.Image.new_from_icon_name("applications-system-symbolic", Gtk.IconSize.BUTTON))
|
||||
option_button_box.add(Gtk.Label(get_message("Options")))
|
||||
menu_button = Gtk.MenuButton(popover=popover)
|
||||
menu_button.add(option_button_box)
|
||||
menu_button.show_all()
|
||||
self._right_action_box.pack_end(menu_button, False, False, 0)
|
||||
|
||||
@run_idle
|
||||
def on_receive_data(self, item):
|
||||
@@ -723,6 +778,14 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
|
||||
self.receive_services()
|
||||
|
||||
def on_source_changed(self, itme):
|
||||
is_kos = itme.get_active_id() == SatelliteSource.KINGOFSAT.name
|
||||
self._kos_options_box.set_sensitive(is_kos)
|
||||
if not is_kos:
|
||||
self._kos_bq_groups_switch.set_active(False)
|
||||
self._kos_bq_lang_switch.set_active(False)
|
||||
self._kos_options_box.set_tooltip_text(None if is_kos else get_message("KingOfSat only!"))
|
||||
|
||||
@run_task
|
||||
def receive_services(self):
|
||||
self.is_download = True
|
||||
@@ -791,6 +854,7 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
log(f"Getting services error: {e} [{t_names.get(futures[future])}]")
|
||||
|
||||
appender.send("-" * 75 + "\n")
|
||||
services = OrderedDict({s.fav_id: s for s in services}).values()
|
||||
appender.send(f"Consumed: {time.time() - start:0.0f}s, {len(services)} services received.")
|
||||
|
||||
try:
|
||||
@@ -801,10 +865,38 @@ class ServicesUpdateDialog(UpdateDialog):
|
||||
except ValueError as e:
|
||||
log(f"ServicesUpdateDialog [on receive data] error: {e}")
|
||||
else:
|
||||
self._callback(srvs)
|
||||
bouquets = None
|
||||
if self._source_box.get_active_id() == SatelliteSource.KINGOFSAT.name:
|
||||
bouquets = self.get_bouquets(srvs, services)
|
||||
|
||||
def c_filter(s):
|
||||
try:
|
||||
return int(s.freq) > 10000
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
self._callback(filter(c_filter, srvs) if self._skip_c_band_switch.get_active() else srvs, bouquets)
|
||||
|
||||
self.is_download = False
|
||||
|
||||
def get_bouquets(self, prepared, services):
|
||||
bouquets = []
|
||||
services = [srv._replace(fav_id=prepared[i].fav_id) for i, srv in enumerate(services)]
|
||||
|
||||
if self._kos_bq_groups_switch.get_active():
|
||||
self.gen_bouquet_group(services, bouquets, lambda s: s[4] or "")
|
||||
if self._kos_bq_lang_switch.get_active():
|
||||
self.gen_bouquet_group(services, bouquets, lambda s: s[5] or "")
|
||||
|
||||
return Bouquets("", BqType.TV.value, bouquets),
|
||||
|
||||
def gen_bouquet_group(self, services, bouquets, grouper):
|
||||
""" Generates bouquets depending on <grouper>. """
|
||||
s_type = BqServiceType.DEFAULT
|
||||
[bouquets.append(Bouquet(name=g[0], type=BqType.TV.name,
|
||||
services=[BouquetService(None, s_type, s.fav_id, 0) for s in g[1]])) for g in
|
||||
groupby(sorted(services, key=grouper), key=grouper) if g[0]]
|
||||
|
||||
@run_task
|
||||
def get_sat_list(self, src, callback):
|
||||
sat_src = SatelliteSource.LYNGSAT
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.22.2
|
||||
<!-- Generated with glade 3.38.2
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018-2022 Dmitriy Yefremov
|
||||
Copyright (c) 2018-2023 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
|
||||
@@ -27,37 +27,37 @@ Author: Dmitriy Yefremov
|
||||
|
||||
-->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<requires lib="gtk+" version="3.22"/>
|
||||
<!-- interface-css-provider-path style.css -->
|
||||
<!-- interface-license-type mit -->
|
||||
<!-- interface-name DemonEditor -->
|
||||
<!-- interface-copyright 2018-2022 Dmitriy Yefremov -->
|
||||
<!-- interface-copyright 2018-2023 Dmitriy Yefremov -->
|
||||
<!-- interface-authors Dmitriy Yefremov -->
|
||||
<object class="GtkAdjustment" id="pos_adjustment">
|
||||
<property name="upper">180</property>
|
||||
<property name="step_increment">0.10000000000000001</property>
|
||||
<property name="page_increment">10</property>
|
||||
<property name="step-increment">0.10</property>
|
||||
<property name="page-increment">10</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="pos_adjustment2">
|
||||
<property name="upper">180</property>
|
||||
<property name="step_increment">0.10000000000000001</property>
|
||||
<property name="page_increment">10</property>
|
||||
<property name="step-increment">0.10</property>
|
||||
<property name="page-increment">10</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="remove_selection_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-undo</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-undo</property>
|
||||
</object>
|
||||
<object class="GtkMenu" id="satellites_update_popup_menu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="select_all_popup_item">
|
||||
<property name="label">gtk-select-all</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="use-underline">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="activate" handler="on_select_all" object="sat_update_tree_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
@@ -65,29 +65,29 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkImageMenuItem" id="unselect_all_popup_item">
|
||||
<property name="label" translatable="yes">Remove selection</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="image">remove_selection_image</property>
|
||||
<property name="use_stock">False</property>
|
||||
<property name="use-stock">False</property>
|
||||
<signal name="activate" handler="on_unselect_all" object="sat_update_tree_view" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<object class="GtkImage" id="sat_receive_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">network-receive-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">network-receive-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="sat_update_cancel_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">window-close-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">window-close-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="sat_update_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">emblem-synchronizing-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">emblem-synchronizing-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkListStore" id="side_store">
|
||||
@@ -119,7 +119,7 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="update_sat_list_model_filter">
|
||||
<property name="child_model">update_sat_list_store</property>
|
||||
<property name="child-model">update_sat_list_store</property>
|
||||
</object>
|
||||
<object class="GtkTreeModelSort" id="update_sat_list_model_sort">
|
||||
<property name="model">update_sat_list_model_filter</property>
|
||||
@@ -151,42 +151,39 @@ Author: Dmitriy Yefremov
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkWindow" id="satellites_update_window">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">Satellites update</property>
|
||||
<property name="modal">True</property>
|
||||
<property name="window_position">center-on-parent</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="skip_taskbar_hint">True</property>
|
||||
<property name="skip_pager_hint">True</property>
|
||||
<property name="window-position">center-on-parent</property>
|
||||
<property name="destroy-with-parent">True</property>
|
||||
<property name="skip-taskbar-hint">True</property>
|
||||
<property name="skip-pager-hint">True</property>
|
||||
<signal name="delete-event" handler="on_quit" swapped="no"/>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="satellites_update_main_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_update_header">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="satellites_update_header_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">15</property>
|
||||
<property name="margin_right">15</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">15</property>
|
||||
<property name="margin-right">15</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="source_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="tooltip_text" translatable="yes">Source</property>
|
||||
<property name="active">0</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="tooltip-text" translatable="yes">Source</property>
|
||||
<property name="active-id">KINGOFSAT</property>
|
||||
<items>
|
||||
<item id="LYNGSAT" translatable="yes">LyngSat</item>
|
||||
<item id="KINGOFSAT" translatable="yes">KingOfSat</item>
|
||||
@@ -199,19 +196,19 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="sat_update_right_action_box">
|
||||
<object class="GtkButtonBox" id="sat_update_left_action_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="layout-style">expand</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="sat_update_button">
|
||||
<property name="label" translatable="yes">Update</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Update</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Update</property>
|
||||
<property name="image">sat_update_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<signal name="clicked" handler="on_update_satellites_list" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -221,15 +218,15 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="receive_data_button">
|
||||
<property name="label" translatable="yes">Receive</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Receive</property>
|
||||
<property name="image">sat_receive_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_receive_data" swapped="no"/>
|
||||
<object class="GtkButton" id="cancel_data_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Cancel</property>
|
||||
<property name="image">sat_update_cancel_image</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<signal name="clicked" handler="on_cancel_receive" swapped="no"/>
|
||||
<accelerator key="z" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -238,15 +235,16 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancel_data_button">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Cancel</property>
|
||||
<property name="image">sat_update_cancel_image</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_cancel_receive" swapped="no"/>
|
||||
<accelerator key="z" signal="clicked" modifiers="Primary"/>
|
||||
<object class="GtkButton" id="receive_data_button">
|
||||
<property name="label" translatable="yes">Receive</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Receive</property>
|
||||
<property name="image">sat_receive_image</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<signal name="clicked" handler="on_receive_data" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -262,63 +260,75 @@ Author: Dmitriy Yefremov
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButtonBox" id="sat_update_left_action_box">
|
||||
<object class="GtkBox" id="sat_update_right_action_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">expand</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="sat_update_filter_button">
|
||||
<object class="GtkButtonBox" id="sat_update_fs_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Filter</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="toggled" handler="on_filter_toggled" swapped="no"/>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="layout-style">expand</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="sat_update_filter_image">
|
||||
<object class="GtkToggleButton" id="sat_update_filter_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-find-replace-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Filter</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<signal name="toggled" handler="on_filter_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="sat_update_filter_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-find-replace-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="sat_update_find_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="tooltip-text" translatable="yes">Find</property>
|
||||
<property name="always-show-image">True</property>
|
||||
<signal name="toggled" handler="on_find_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="sat_update_search_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">edit-find-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="f" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<accelerator key="f" signal="clicked" modifiers="GDK_SHIFT_MASK | Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="sat_update_find_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="tooltip_text" translatable="yes">Find</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="toggled" handler="on_find_toggled" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkImage" id="sat_update_search_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">edit-find-symbolic</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<accelerator key="f" signal="clicked" modifiers="Primary"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
@@ -341,21 +351,21 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSearchBar" id="sat_update_search_bar">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="search_bar_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">2</property>
|
||||
<property name="margin-bottom">2</property>
|
||||
<child>
|
||||
<object class="GtkSearchEntry" id="sat_update_search_entry">
|
||||
<property name="width_request">200</property>
|
||||
<property name="width-request">200</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="primary_icon_name">edit-find-symbolic</property>
|
||||
<property name="primary_icon_activatable">False</property>
|
||||
<property name="primary_icon_sensitive">False</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="primary-icon-name">edit-find-symbolic</property>
|
||||
<property name="primary-icon-activatable">False</property>
|
||||
<property name="primary-icon-sensitive">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -367,13 +377,13 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkButton" id="sat_update_search_down_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkArrow" id="arrow1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="arrow_type">down</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="arrow-type">down</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -387,13 +397,13 @@ Author: Dmitriy Yefremov
|
||||
<object class="GtkButton" id="sat_update_search_up_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<child>
|
||||
<object class="GtkArrow" id="arrow2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="arrow_type">up</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="arrow-type">up</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -417,44 +427,45 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSearchBar" id="sat_update_filter_bar">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<!-- n-columns=7 n-rows=1 -->
|
||||
<object class="GtkGrid" id="source_header_grid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">2</property>
|
||||
<property name="margin_bottom">2</property>
|
||||
<property name="column_spacing">2</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-top">2</property>
|
||||
<property name="margin-bottom">2</property>
|
||||
<property name="column-spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="from_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">From:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">0</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="from_pos_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="input-purpose">number</property>
|
||||
<property name="adjustment">pos_adjustment</property>
|
||||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="changed" handler="on_filter" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">1</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="filter_from_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="model">side_store</property>
|
||||
<property name="active">0</property>
|
||||
<signal name="changed" handler="on_filter" swapped="no"/>
|
||||
@@ -466,40 +477,40 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">2</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">2</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="to_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">To:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">3</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">3</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="to_pos_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="input_purpose">number</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="input-purpose">number</property>
|
||||
<property name="adjustment">pos_adjustment2</property>
|
||||
<property name="digits">1</property>
|
||||
<property name="numeric">True</property>
|
||||
<signal name="changed" handler="on_filter" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">4</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">4</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBox" id="filter_to_combo_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="model">side_store</property>
|
||||
<property name="active">0</property>
|
||||
<signal name="changed" handler="on_filter" swapped="no"/>
|
||||
@@ -511,22 +522,22 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">5</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">5</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="filter_apply_button">
|
||||
<property name="label">gtk-apply</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="receives-default">True</property>
|
||||
<property name="use-stock">True</property>
|
||||
<signal name="clicked" handler="on_filter" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left_attach">6</property>
|
||||
<property name="top_attach">0</property>
|
||||
<property name="left-attach">6</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
@@ -541,40 +552,40 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkPaned" id="sat_update_main_paned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="wide-handle">True</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="sat_update_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.49000000953674316</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_update_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="sat_update_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="sat_update_tree_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">update_sat_list_model_sort</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<property name="search-column">0</property>
|
||||
<property name="activate-on-single-click">True</property>
|
||||
<signal name="button-press-event" handler="on_popup_menu" object="satellites_update_popup_menu" swapped="no"/>
|
||||
<signal name="select-all" handler="on_select_all" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
@@ -589,10 +600,10 @@ Author: Dmitriy Yefremov
|
||||
<property name="clickable">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="sort_column_id">0</property>
|
||||
<property name="sort-column-id">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="upd_satellite_cellrenderertext">
|
||||
<property name="xalign">0.0099999997764825821</property>
|
||||
<property name="xalign">0.009999999776482582</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<attributes>
|
||||
@@ -607,7 +618,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="clickable">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="sort_column_id">1</property>
|
||||
<property name="sort-column-id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="upd_position_cellrenderertext">
|
||||
<property name="xalign">0.49000000953674316</property>
|
||||
@@ -624,7 +635,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="clickable">True</property>
|
||||
<property name="alignment">0.5</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="sort_column_id">2</property>
|
||||
<property name="sort-column-id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="upd_type_cellrenderertext">
|
||||
<property name="xalign">0.49000000953674316</property>
|
||||
@@ -652,7 +663,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="title" translatable="yes">Selected</property>
|
||||
<property name="clickable">True</property>
|
||||
<property name="reorderable">True</property>
|
||||
<property name="sort_column_id">4</property>
|
||||
<property name="sort-column-id">4</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererToggle" id="upd_selected_cellrenderer">
|
||||
<signal name="toggled" handler="on_satellite_toggled" swapped="no"/>
|
||||
@@ -675,15 +686,15 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_update_status_box">
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">10</property>
|
||||
<property name="margin-right">10</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="satellites_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -694,7 +705,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="satellites_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -715,7 +726,7 @@ Author: Dmitriy Yefremov
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="sat_update_frame_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Satellites</property>
|
||||
</object>
|
||||
</child>
|
||||
@@ -727,37 +738,37 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkPaned" id="sat_update_tr_paned">
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="wide_handle">True</property>
|
||||
<property name="wide-handle">True</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="sat_update_tr_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.49000000953674316</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_update_tr_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="sat_update_tr_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="sat_update_tr_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">update_transponder_store</property>
|
||||
<property name="search_column">0</property>
|
||||
<property name="activate_on_single_click">True</property>
|
||||
<property name="search-column">0</property>
|
||||
<property name="activate-on-single-click">True</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
@@ -768,7 +779,7 @@ Author: Dmitriy Yefremov
|
||||
<property name="alignment">0.5</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="upd_tr_renderer">
|
||||
<property name="xalign">0.0099999997764825821</property>
|
||||
<property name="xalign">0.009999999776482582</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
@@ -813,15 +824,15 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_update_tr_status_box">
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">10</property>
|
||||
<property name="margin-right">10</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="transponders_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -832,7 +843,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="transponders_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -853,7 +864,7 @@ Author: Dmitriy Yefremov
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="sat_update_tr_frame_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Transponders</property>
|
||||
</object>
|
||||
</child>
|
||||
@@ -866,30 +877,30 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkFrame" id="sat_update_srv_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0.49000000953674316</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label-xalign">0.49000000953674316</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_update_srv_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">5</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="margin-top">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="sat_update_srv_scrolled_window">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="sat_update_srv_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="model">update_service_store</property>
|
||||
<property name="search_column">1</property>
|
||||
<property name="search-column">1</property>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection"/>
|
||||
</child>
|
||||
@@ -908,7 +919,7 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="srv_service_renderer">
|
||||
<property name="xalign">0.0099999997764825821</property>
|
||||
<property name="xalign">0.009999999776482582</property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
@@ -986,15 +997,15 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkBox" id="sat_update_srv_status_box">
|
||||
<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="can-focus">False</property>
|
||||
<property name="margin-left">10</property>
|
||||
<property name="margin-right">10</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="services_count_image">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">document-properties-symbolic</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="icon-name">document-properties-symbolic</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1005,7 +1016,7 @@ Author: Dmitriy Yefremov
|
||||
<child>
|
||||
<object class="GtkLabel" id="services_count_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
@@ -1029,7 +1040,7 @@ Author: Dmitriy Yefremov
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="sat_update_srv_frame_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Services</property>
|
||||
</object>
|
||||
</child>
|
||||
@@ -1054,23 +1065,23 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="log_frame">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">4</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="margin_bottom">5</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="margin-left">4</property>
|
||||
<property name="margin-right">5</property>
|
||||
<property name="margin-bottom">5</property>
|
||||
<property name="label-xalign">0</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="log_info_bar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="message_type">other</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="message-type">other</property>
|
||||
<property name="show-close-button">True</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="log_info_bar_button_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<property name="layout-style">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1080,20 +1091,20 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="log_info_bar_box">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="text_view_scrolled_window">
|
||||
<property name="height_request">120</property>
|
||||
<property name="height-request">120</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="shadow-type">in</property>
|
||||
<child>
|
||||
<object class="GtkTextView" id="text_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="editable">False</property>
|
||||
<property name="left_margin">5</property>
|
||||
<property name="right_margin">5</property>
|
||||
<property name="left-margin">5</property>
|
||||
<property name="right-margin">5</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -1121,15 +1132,15 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkInfoBar" id="sat_update_info_bar">
|
||||
<property name="app_paintable">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="show_close_button">True</property>
|
||||
<property name="app-paintable">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="show-close-button">True</property>
|
||||
<signal name="response" handler="on_info_bar_close" swapped="no"/>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox" id="infobar-action_area2">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="layout_style">end</property>
|
||||
<property name="layout-style">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
@@ -1139,12 +1150,12 @@ Author: Dmitriy Yefremov
|
||||
</child>
|
||||
<child internal-child="content_area">
|
||||
<object class="GtkBox" id="infobar-content_area2">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="spacing">16</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="info_bar_message_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Info</property>
|
||||
</object>
|
||||
<packing>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#!/bin/bash
|
||||
VER="3.2.3_Beta"
|
||||
VER="3.5.0_Beta"
|
||||
B_PATH="dist/DemonEditor"
|
||||
DEB_PATH="$B_PATH/usr/share/demoneditor"
|
||||
|
||||
mkdir -p $B_PATH
|
||||
cp -TRv deb $B_PATH
|
||||
rsync --exclude=app/ui/lang --exclude=app/ui/icons --exclude=__pycache__ -arv ../../app $DEB_PATH
|
||||
rsync --exclude=__pycache__ -arv ../../extensions $DEB_PATH
|
||||
|
||||
cd dist
|
||||
fakeroot dpkg-deb -Zxz --build DemonEditor
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: demon-editor
|
||||
Version: 3.2.3-Beta
|
||||
Version: 3.5.0-Beta
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -24,7 +24,8 @@ ui_files = [('app/ui/*.glade', 'ui'),
|
||||
('app/ui/epg/*.glade', 'ui/epg'),
|
||||
('app/ui/xml/*.glade', 'ui/xml'),
|
||||
('app/ui/lang*', 'share/locale'),
|
||||
('app/ui/icons*', 'share/icons')
|
||||
('app/ui/icons*', 'share/icons'),
|
||||
('extensions/*', 'extensions')
|
||||
]
|
||||
|
||||
a = Analysis([EXE_NAME],
|
||||
@@ -80,7 +81,7 @@ app = BUNDLE(coll,
|
||||
'CFBundleGetInfoString': "Enigma2 channel and satellite editor",
|
||||
'LSApplicationCategoryType': 'public.app-category.utilities',
|
||||
'LSMinimumSystemVersion': '10.13',
|
||||
'CFBundleShortVersionString': f"3.2.3.{BUILD_DATE} Beta",
|
||||
'CFBundleShortVersionString': f"3.5.0.{BUILD_DATE} Beta",
|
||||
'NSHumanReadableCopyright': u"Copyright © 2023, Dmitriy Yefremov",
|
||||
'NSRequiresAquaSystemAppearance': 'false',
|
||||
'NSHighResolutionCapable': 'true'
|
||||
|
||||
@@ -21,7 +21,8 @@ ui_files = [('app\\ui\\*.glade', 'ui'),
|
||||
('app\\ui\\epg\\*.glade', 'ui\\epg'),
|
||||
('app\\ui\\xml\\*.glade', 'ui\\xml'),
|
||||
('app\\ui\\lang*', 'share\\locale'),
|
||||
('app\\ui\\icons*', 'share\\icons')
|
||||
('app\\ui\\icons*', 'share\\icons'),
|
||||
('extensions\\*', 'extensions')
|
||||
]
|
||||
|
||||
|
||||
|
||||
10
extensions/README.md
Normal file
10
extensions/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
Extension packages must be located in the following paths:
|
||||
``` app/ui/extensions```, ``` your data path/tools/extensions ```.
|
||||
|
||||
For builds:
|
||||
``` Program Root\extensions ```
|
||||
|
||||
Extensions and examples can be found [here](https://github.com/DYefremov/demoneditor-extensions).
|
||||
The possibilities of extending the API, as well as the creation and publication of the necessary extensions, can be discussed there.
|
||||
|
||||
### Pull requests for extensions are not accepted here!
|
||||
97
extensions/__init__.py
Normal file
97
extensions/__init__.py
Normal file
@@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2023 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
|
||||
#
|
||||
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
CONFIG_PATH = f"{Path.home()}{os.sep}.config{os.sep}demon-editor{os.sep}extensions{os.sep}"
|
||||
|
||||
|
||||
class Singleton(type):
|
||||
_INSTANCE = None
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if not cls._INSTANCE:
|
||||
cls._INSTANCE = type.__call__(cls, *args, **kwargs)
|
||||
return cls._INSTANCE
|
||||
|
||||
|
||||
class BaseExtension(metaclass=Singleton):
|
||||
""" Base extension (plugin) class. """
|
||||
# The label that will be displayed in the "Tools" menu.
|
||||
LABEL = "Base extension"
|
||||
VERSION = "1.0"
|
||||
|
||||
_LOGGER_NAME = "main_logger"
|
||||
|
||||
def __init__(self, app):
|
||||
# Current application instance.
|
||||
# It can be used all public methods, properties or signals.
|
||||
self.app = app
|
||||
self._config_path = f"{CONFIG_PATH}{self.__class__.__name__}{os.sep}config"
|
||||
|
||||
self.log(f"Extension initialized...")
|
||||
|
||||
def exec(self):
|
||||
""" Triggers an action for the given extension.
|
||||
|
||||
E.g. shows a dialog or runs an external script.
|
||||
"""
|
||||
self.app.show_info_message(f"Hello from {self.__class__.__name__} class!")
|
||||
|
||||
def log(self, message, level=logging.ERROR):
|
||||
""" Shows log messages. """
|
||||
logging.getLogger(self._LOGGER_NAME).log(level, f"[{self.__class__.__name__}] {message}")
|
||||
|
||||
def reset_config(self):
|
||||
path = Path(self._config_path)
|
||||
if path.is_file():
|
||||
path.unlink()
|
||||
|
||||
@property
|
||||
def config(self) -> dict:
|
||||
if not Path(self._config_path).is_file():
|
||||
return {}
|
||||
|
||||
with open(self._config_path, "r", encoding="utf-8") as config_file:
|
||||
try:
|
||||
return json.load(config_file)
|
||||
except ValueError as e:
|
||||
self.log(f"Configuration load error: {e}")
|
||||
return {}
|
||||
|
||||
@config.setter
|
||||
def config(self, value: dict):
|
||||
Path(self._config_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
with open(self._config_path, "w", encoding="utf-8") as config_file:
|
||||
json.dump(value, config_file, indent=" ")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2021 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2023 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
@@ -1416,3 +1416,54 @@ msgstr "Здаліць сцяжок \"New\""
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Групаваць па"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Замяніць існуючыя"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Ужо існуе"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Уключыць неабмежаваны буфер капіявання"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Улучае неабмежаваны буфер капіявання для ўкладкі букетаў."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Пачатак"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Сканчэнне"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Уключыць падтрымку пашырэнняў"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "Пасля загрузкі змен можа запатрабавацца перазапуск рэсівера!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Выдаліць дублікаты"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Выдалена"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Улучае перазапіс існых сэрвісаў асноўнага спіса."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Улучае пропуск імпарту сэрвісаў з lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Толькі дадзеныя букетаў"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Стварыць букеты па катэгорыях"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Стварыць букеты па рэгіёнах"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "Прапусціць C-дыяпазон"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Аўтаматычная ўсталёўка імя, абранага ў спісе букетаў."
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
# Charly, 2019.
|
||||
# Dmitriy Yefremov, 2020-2021.
|
||||
# Thomas Schmidt, 2021
|
||||
# Dmitriy Yefremov, 2020-2023.
|
||||
# Thomas Schmidt, 2021.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Last-Translator: Dmitriy Yefremov\n"
|
||||
@@ -1430,3 +1430,54 @@ msgstr "Das \"Neu\"-Flag entfernen"
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Gruppieren nach"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Vorhandene ersetzen"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Bereits vorhanden"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Unbegrenzte Zwischenablage aktivieren"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Aktiviert unbegrenzte Zwischenablage für die Bouquets-Tab."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Anfangszeit"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Endzeit"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Erweiterungen Unterstützung aktivieren"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "Nach dem Hochladen der Änderungen muss den Receiver eventuell komplett neu starten!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Duplikate entfernen"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Entfernt"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Ermöglicht das Überschreiben vorhandener Hauptlistendienste."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Ermöglicht das Überspringen von Dienstimporten aus lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Nur Bouquets-Daten"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Erstellen Kategories-Bouquets"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Erstellen Regionale-Bouquets"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "C-Band überspringen"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Stellen in der Bouquet-Liste ausgewählten Namen automatisch ein."
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
# Massimo Pissarello <mapi68@gmail.com>, 2022.
|
||||
# Massimo Pissarello <mapi68@gmail.com>, 2022, 2023.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"PO-Revision-Date: 2022-12-14 06:37+0100\n"
|
||||
"PO-Revision-Date: 2023-03-06 13:22+0100\n"
|
||||
"Last-Translator: Massimo Pissarello <mapi68@gmail.com>\n"
|
||||
"Language-Team: Italian <>\n"
|
||||
"Language: it\n"
|
||||
@@ -14,7 +14,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Lokalize 22.12.0\n"
|
||||
"X-Generator: Lokalize 22.12.3\n"
|
||||
|
||||
msgid "translator-credits"
|
||||
msgstr "Massimo Pissarello"
|
||||
@@ -1230,6 +1230,9 @@ msgstr "Dimensione"
|
||||
msgid "Date"
|
||||
msgstr "Data"
|
||||
|
||||
msgid "Attr."
|
||||
msgstr "Attr"
|
||||
|
||||
msgid "Toggle display position"
|
||||
msgstr "Commuta posizione visualizzazione"
|
||||
|
||||
@@ -1472,3 +1475,56 @@ msgstr "Rimuovi il flag \"Nuovo\""
|
||||
msgid "Group by"
|
||||
msgstr "Raggruppa per"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Sostituisci esistente"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Esiste già"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Abilita buffer di copia illimitato"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Abilita buffer di copia illimitato per la scheda bouquet."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Ora di inizio"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Ora di fine"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Abilita supporto estensioni"
|
||||
|
||||
msgid ""
|
||||
"After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr ""
|
||||
"Dopo aver caricato le modifiche potrebbe essere necessario riavviare"
|
||||
" completamente il ricevitore!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Rimuovi duplicati"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Rimosso"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Consente di sovrascrivere i servizi esistenti dell'elenco principale."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Consente di saltare l'importazione dei servizi da lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Bouquet solo dati"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Crea bouquet categorie"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Crea bouquet regionali"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "Salta banda C"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Imposta automaticamente il nome selezionato nell'elenco dei bouquet."
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright (C) 2018-2022 Dmitriy Yefremov
|
||||
# Copyright (C) 2018-2023 Dmitriy Yefremov
|
||||
# This file is distributed under the MIT license.
|
||||
#
|
||||
#
|
||||
@@ -1413,3 +1413,54 @@ msgstr "Очистить флаг \"New\""
|
||||
|
||||
msgid "Group by"
|
||||
msgstr "Группировать по"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Заменить существующие"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Уже существует"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Включить неограниченный буфер копирования"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Включает неограниченный буфер копирования для вкладки букетов."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Начало"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Окончание"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Включить поддержку расширений"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "После загрузки изменений может потребоваться перезапуск ресивера!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Удалить дубликаты"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Удалено"
|
||||
|
||||
msgid "Enables overwriting existing main list services."
|
||||
msgstr "Включает перезапись существующих сервисов основного списка."
|
||||
|
||||
msgid "Enables skipping services import from lamedb."
|
||||
msgstr "Включает пропуск импорта сервисов из lamedb."
|
||||
|
||||
msgid "Bouquets data only"
|
||||
msgstr "Только данные букетов"
|
||||
|
||||
msgid "Create Category bouquets"
|
||||
msgstr "Создать букеты по категориям"
|
||||
|
||||
msgid "Create Regional bouquets"
|
||||
msgstr "Создать букеты по регионам"
|
||||
|
||||
msgid "Skip C-band"
|
||||
msgstr "Пропустить C-диапазон"
|
||||
|
||||
msgid "Automatically set the name selected in the bouquet list."
|
||||
msgstr "Автоматическая установка имени, выбранного в списке букетов."
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: DemonEditor\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-04-16 15:59+0300\n"
|
||||
"PO-Revision-Date: 2023-01-13 21:54+0300\n"
|
||||
"PO-Revision-Date: 2023-02-23 14:22+0300\n"
|
||||
"Last-Translator: audi06_19 <info@dreamosat-forum.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: tr\n"
|
||||
@@ -11,7 +11,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Poedit 3.0.1\n"
|
||||
"X-Generator: Poedit 3.2.2\n"
|
||||
|
||||
msgid "translator-credits"
|
||||
msgstr "audi06_19 <info@dreamosat-forum.com>"
|
||||
@@ -1446,4 +1446,34 @@ msgid "Clear \"New\" flag"
|
||||
msgstr "\"Yeni\" bayrağını temizleyin"
|
||||
|
||||
msgid "Group by"
|
||||
msgstr ""
|
||||
msgstr "Göre gruplandır"
|
||||
|
||||
msgid "Replace existing"
|
||||
msgstr "Mevcut olanı değiştir"
|
||||
|
||||
msgid "Already exists"
|
||||
msgstr "Zaten var"
|
||||
|
||||
msgid "Enable unlimited copy buffer"
|
||||
msgstr "Sınırsız kopya arabelleğini etkinleştir"
|
||||
|
||||
msgid "Enables unlimited copy buffer for the bouquets tab."
|
||||
msgstr "Buketler sekmesi için sınırsız kopya arabelleğini etkinleştirir."
|
||||
|
||||
msgid "Start time"
|
||||
msgstr "Başlangıç saati"
|
||||
|
||||
msgid "End time"
|
||||
msgstr "Bitiş zamanı"
|
||||
|
||||
msgid "Enable extensions support"
|
||||
msgstr "Uzantı desteğini etkinleştir"
|
||||
|
||||
msgid "After uploading the changes you may need to completely reboot the receiver!"
|
||||
msgstr "Değişiklikleri yükledikten sonra, alıcıyı tamamen yeniden başlatmanız gerekebilir!"
|
||||
|
||||
msgid "Remove duplicates"
|
||||
msgstr "Kopyaları kaldır"
|
||||
|
||||
msgid "Removed"
|
||||
msgstr "Kaldırıldı"
|
||||
|
||||
Reference in New Issue
Block a user