paths settings rework

This commit is contained in:
DYefremov
2021-08-30 15:04:15 +03:00
parent b584126bff
commit 7da3c0fd94
16 changed files with 450 additions and 198 deletions

View File

@@ -328,7 +328,7 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=log, fil
with UtfFTP(host=settings.host, user=settings.user, passwd=settings.password) as ftp: with UtfFTP(host=settings.host, user=settings.user, passwd=settings.password) as ftp:
ftp.encoding = "utf-8" ftp.encoding = "utf-8"
callback("FTP OK.\n") callback("FTP OK.\n")
save_path = settings.data_local_path save_path = settings.profile_data_path
os.makedirs(os.path.dirname(save_path), exist_ok=True) os.makedirs(os.path.dirname(save_path), exist_ok=True)
# bouquets # bouquets
if download_type is DownloadType.ALL or download_type is DownloadType.BOUQUETS: if download_type is DownloadType.ALL or download_type is DownloadType.BOUQUETS:
@@ -342,7 +342,7 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=log, fil
ftp.download_xml(save_path, settings.satellites_xml_path, WEB_TV_XML_FILE, callback) ftp.download_xml(save_path, settings.satellites_xml_path, WEB_TV_XML_FILE, callback)
if download_type is DownloadType.PICONS: if download_type is DownloadType.PICONS:
picons_path = settings.picons_local_path picons_path = settings.profile_picons_path
os.makedirs(os.path.dirname(picons_path), exist_ok=True) os.makedirs(os.path.dirname(picons_path), exist_ok=True)
ftp.download_picons(settings.picons_path, picons_path, callback, files_filter) ftp.download_picons(settings.picons_path, picons_path, callback, files_filter)
# epg.dat # epg.dat
@@ -362,7 +362,7 @@ def download_data(*, settings, download_type=DownloadType.ALL, callback=log, fil
def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False, def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False,
callback=log, done_callback=None, use_http=False, files_filter=None): callback=log, done_callback=None, use_http=False, files_filter=None):
s_type = settings.setting_type s_type = settings.setting_type
data_path = settings.data_local_path data_path = settings.profile_data_path
host = settings.host host = settings.host
base_url = "http{}://{}:{}".format("s" if settings.http_use_ssl else "", host, settings.http_port) base_url = "http{}://{}:{}".format("s" if settings.http_use_ssl else "", host, settings.http_port)
url = "{}/web/".format(base_url) url = "{}/web/".format(base_url)
@@ -428,7 +428,7 @@ def upload_data(*, settings, download_type=DownloadType.ALL, remove_unused=False
ftp.upload_files(data_path, DATA_FILES_LIST, callback) ftp.upload_files(data_path, DATA_FILES_LIST, callback)
if download_type is DownloadType.PICONS: if download_type is DownloadType.PICONS:
ftp.upload_picons(settings.picons_local_path, settings.picons_path, callback, files_filter) ftp.upload_picons(settings.profile_picons_path, settings.picons_path, callback, files_filter)
if tn and not use_http: if tn and not use_http:
# resume enigma or restart neutrino # resume enigma or restart neutrino

View File

@@ -1,3 +1,31 @@
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 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 copy import copy
import json import json
import locale import locale
@@ -13,7 +41,7 @@ SEP = os.sep
HOME_PATH = str(Path.home()) HOME_PATH = str(Path.home())
CONFIG_PATH = HOME_PATH + "{}.config{}demon-editor{}".format(SEP, SEP, SEP) CONFIG_PATH = HOME_PATH + "{}.config{}demon-editor{}".format(SEP, SEP, SEP)
CONFIG_FILE = CONFIG_PATH + "config.json" CONFIG_FILE = CONFIG_PATH + "config.json"
DATA_PATH = HOME_PATH + "{}DemonEditor{}data{}".format(SEP, SEP, SEP) DATA_PATH = HOME_PATH + "{}DemonEditor{}".format(SEP, SEP)
GTK_PATH = os.environ.get("GTK_PATH", None) GTK_PATH = os.environ.get("GTK_PATH", None)
IS_DARWIN = sys.platform == "darwin" IS_DARWIN = sys.platform == "darwin"
@@ -23,6 +51,31 @@ IS_LINUX = sys.platform == "linux"
class Defaults(Enum): class Defaults(Enum):
""" Default program settings """ """ Default program settings """
USER = "root"
PASSWORD = ""
HOST = "127.0.0.1"
FTP_PORT = "21"
HTTP_PORT = "80"
TELNET_PORT = "23"
HTTP_USE_SSL = False
# Enigma2.
BOX_SERVICES_PATH = "/etc/enigma2/"
BOX_SATELLITE_PATH = "/etc/tuxbox/"
BOX_PICON_PATH = "/usr/share/enigma2/picon/"
BOX_PICON_PATHS = ("/usr/share/enigma2/picon/",
"/media/hdd/picon/",
"/media/usb/picon/",
"/media/mmc/picon/",
"/media/cf/picon/")
# Neutrino.
NEUTRINO_BOX_SERVICES_PATH = "/var/tuxbox/config/zapit/"
NEUTRINO_BOX_SATELLITE_PATH = "/var/tuxbox/config/"
NEUTRINO_BOX_PICON_PATH = "/usr/share/tuxbox/neutrino/icons/logo/"
NEUTRINO_BOX_PICON_PATHS = ("/usr/share/tuxbox/neutrino/icons/logo/",)
# Paths.
BACKUP_PATH = "{}backup{}".format(DATA_PATH, SEP)
PICON_PATH = "{}picons{}".format(DATA_PATH, SEP)
DEFAULT_PROFILE = "default" DEFAULT_PROFILE = "default"
BACKUP_BEFORE_DOWNLOADING = True BACKUP_BEFORE_DOWNLOADING = True
BACKUP_BEFORE_SAVE = True BACKUP_BEFORE_SAVE = True
@@ -43,64 +96,6 @@ class Defaults(Enum):
RECORDS_PATH = DATA_PATH + "records{}".format(SEP) RECORDS_PATH = DATA_PATH + "records{}".format(SEP)
ACTIVATE_TRANSCODING = False ACTIVATE_TRANSCODING = False
ACTIVE_TRANSCODING_PRESET = "720p TV{}device".format(SEP) ACTIVE_TRANSCODING_PRESET = "720p TV{}device".format(SEP)
ENIGMA_2_PICONS_PATHS = ("/usr/share/enigma2/picon/",
"/media/hdd/picon/",
"/media/usb/picon/",
"/media/mmc/picon/",
"/media/cf/picon/")
NEUTRINO_PICONS_PATHS = ("/usr/share/tuxbox/neutrino/icons/logo/",)
def get_settings():
if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
write_settings(get_default_settings())
with open(CONFIG_FILE, "r", encoding="utf-8") as config_file:
return json.load(config_file)
def get_default_settings(profile_name="default"):
def_settings = SettingsType.ENIGMA_2.get_default_settings()
set_local_paths(def_settings, profile_name)
return {
"version": 1,
"default_profile": Defaults.DEFAULT_PROFILE.value,
"profiles": {profile_name: def_settings},
"v5_support": Defaults.V5_SUPPORT.value,
"http_api_support": Defaults.HTTP_API_SUPPORT.value,
"enable_yt_dl": Defaults.ENABLE_YT_DL.value,
"enable_send_to": Defaults.ENABLE_SEND_TO.value,
"use_colors": Defaults.USE_COLORS.value,
"new_color": Defaults.NEW_COLOR.value,
"extra_color": Defaults.EXTRA_COLOR.value,
"fav_click_mode": Defaults.FAV_CLICK_MODE.value,
"profile_folder_is_default": Defaults.PROFILE_FOLDER_DEFAULT.value,
"records_path": Defaults.RECORDS_PATH.value
}
def get_default_transcoding_presets():
return {"720p TV/device": {"vcodec": "h264", "vb": "1500", "width": "1280", "height": "720", "acodec": "mp3",
"ab": "192", "channels": "2", "samplerate": "44100", "scodec": "none"},
"1080p TV/device": {"vcodec": "h264", "vb": "3500", "width": "1920", "height": "1080", "acodec": "mp3",
"ab": "192", "channels": "2", "samplerate": "44100", "scodec": "none"}}
def write_settings(config):
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
with open(CONFIG_FILE, "w", encoding="utf-8") as config_file:
json.dump(config, config_file, indent=" ")
def set_local_paths(settings, profile_name, data_path=DATA_PATH, use_profile_folder=False):
settings["data_local_path"] = "{}{}{}".format(data_path, profile_name, SEP)
if use_profile_folder:
settings["picons_local_path"] = "{}{}{}{}{}".format(data_path, profile_name, SEP, "picons", SEP)
settings["backup_local_path"] = "{}{}{}{}{}".format(data_path, profile_name, SEP, "backup", SEP)
else:
settings["picons_local_path"] = "{}{}{}{}{}".format(data_path, "picons", SEP, profile_name, SEP)
settings["backup_local_path"] = "{}{}{}{}{}".format(data_path, "backup", SEP, profile_name, SEP)
class SettingsType(IntEnum): class SettingsType(IntEnum):
@@ -109,29 +104,35 @@ class SettingsType(IntEnum):
NEUTRINO_MP = 1 NEUTRINO_MP = 1
def get_default_settings(self): def get_default_settings(self):
""" Returns default settings for current type """ """ Returns default settings for current type. """
if self is self.ENIGMA_2: if self is self.ENIGMA_2:
return {"setting_type": self.value, srv_path = Defaults.BOX_SERVICES_PATH.value
"host": "127.0.0.1", "port": "21", "timeout": 5, sat_path = Defaults.BOX_SATELLITE_PATH.value
"user": "root", "password": "root", picons_path = Defaults.BOX_PICON_PATH.value
"http_port": "80", "http_timeout": 5, "http_use_ssl": False, http_timeout = 5
"telnet_port": "23", "telnet_timeout": 5, telnet_timeout = 5
"services_path": "/etc/enigma2/", "user_bouquet_path": "/etc/enigma2/", else:
"satellites_xml_path": "/etc/tuxbox/", "data_local_path": "{}enigma2{}".format(DATA_PATH, SEP), srv_path = Defaults.NEUTRINO_BOX_SERVICES_PATH.value
"picons_path": "/usr/share/enigma2/picon/", sat_path = Defaults.NEUTRINO_BOX_SATELLITE_PATH.value
"picons_local_path": "{}enigma2{}picons{}".format(DATA_PATH, SEP, SEP), picons_path = Defaults.NEUTRINO_BOX_PICON_PATH.value
"backup_local_path": "{}enigma2{}backup{}".format(DATA_PATH, SEP, SEP)} http_timeout = 2
elif self is SettingsType.NEUTRINO_MP: telnet_timeout = 1
return {"setting_type": self,
"host": "127.0.0.1", "port": "21", "timeout": 5, return {"setting_type": self.value,
"user": "root", "password": "root", "host": Defaults.HOST.value,
"http_port": "80", "http_timeout": 2, "http_use_ssl": False, "port": Defaults.FTP_PORT.value,
"telnet_port": "23", "telnet_timeout": 1, "timeout": 5,
"services_path": "/var/tuxbox/config/zapit/", "user_bouquet_path": "/var/tuxbox/config/zapit/", "user": Defaults.USER.value,
"satellites_xml_path": "/var/tuxbox/config/", "data_local_path": DATA_PATH + "neutrino/", "password": Defaults.PASSWORD.value,
"picons_path": "/usr/share/tuxbox/neutrino/icons/logo/", "http_port": Defaults.HTTP_PORT.value,
"picons_local_path": DATA_PATH + "neutrino/picons/", "http_timeout": http_timeout,
"backup_local_path": DATA_PATH + "neutrino/backup/"} "http_use_ssl": Defaults.HTTP_USE_SSL.value,
"telnet_port": Defaults.TELNET_PORT.value,
"telnet_timeout": telnet_timeout,
"services_path": srv_path,
"user_bouquet_path": srv_path,
"satellites_xml_path": sat_path,
"picons_path": picons_path}
class SettingsException(Exception): class SettingsException(Exception):
@@ -151,11 +152,11 @@ class PlayStreamsMode(IntEnum):
class Settings: class Settings:
__INSTANCE = None __INSTANCE = None
__VERSION = 1 __VERSION = 2
def __init__(self, ext_settings=None): def __init__(self, ext_settings=None):
try: try:
settings = ext_settings or get_settings() settings = ext_settings or self.get_settings()
except PermissionError as e: except PermissionError as e:
raise SettingsReadException(e) raise SettingsReadException(e)
@@ -186,22 +187,18 @@ class Settings:
return cls.__INSTANCE return cls.__INSTANCE
def save(self): def save(self):
write_settings(self._settings) self.write_settings(self._settings)
def reset(self, force_write=False): def reset(self, force_write=False):
for k, v in self.setting_type.get_default_settings().items(): for k, v in self.setting_type.get_default_settings().items():
self._cp_settings[k] = v self._cp_settings[k] = v
def_path = DATA_PATH
def_path += "enigma2/" if self.setting_type is SettingsType.ENIGMA_2 else "neutrino/"
set_local_paths(self._cp_settings, self._current_profile, def_path, self.profile_folder_is_default)
if force_write: if force_write:
self.save() self.save()
@staticmethod @staticmethod
def reset_to_default(): def reset_to_default():
write_settings(get_default_settings()) Settings.write_settings(Settings.get_default_settings())
def get_default(self, p_name): def get_default(self, p_name):
""" Returns default value for current settings type """ """ Returns default value for current settings type """
@@ -242,6 +239,10 @@ class Settings:
def default_profile(self, value): def default_profile(self, value):
self._settings["default_profile"] = value self._settings["default_profile"] = value
@property
def current_profile_settings(self):
return self._cp_settings
@property @property
def profiles(self): def profiles(self):
return self._profiles return self._profiles
@@ -368,8 +369,8 @@ class Settings:
@property @property
def picons_paths(self): def picons_paths(self):
if self.setting_type is SettingsType.NEUTRINO_MP: if self.setting_type is SettingsType.NEUTRINO_MP:
return Defaults.NEUTRINO_PICONS_PATHS.value return Defaults.NEUTRINO_BOX_PICON_PATHS.value
return Defaults.ENIGMA_2_PICONS_PATHS.value return Defaults.BOX_PICON_PATHS.value
# ***** Local paths ***** # # ***** Local paths ***** #
@@ -382,28 +383,56 @@ class Settings:
self._settings["profile_folder_is_default"] = value self._settings["profile_folder_is_default"] = value
@property @property
def data_local_path(self): def default_data_path(self):
return self._cp_settings.get("data_local_path", self.get_default("data_local_path")) return self._settings.get("default_data_path", DATA_PATH)
@data_local_path.setter @default_data_path.setter
def data_local_path(self, value): def default_data_path(self, value):
self._cp_settings["data_local_path"] = value self._settings["default_data_path"] = value
@property @property
def picons_local_path(self): def default_backup_path(self):
return self._cp_settings.get("picons_local_path", self.get_default("picons_local_path")) return self._settings.get("default_backup_path", Defaults.BACKUP_PATH.value)
@picons_local_path.setter @default_backup_path.setter
def picons_local_path(self, value): def default_backup_path(self, value):
self._cp_settings["picons_local_path"] = value self._settings["default_backup_path"] = value
@property @property
def backup_local_path(self): def default_picon_path(self):
return self._cp_settings.get("backup_local_path", self.get_default("backup_local_path")) return self._settings.get("default_picon_path", Defaults.PICON_PATH.value)
@backup_local_path.setter @default_picon_path.setter
def backup_local_path(self, value): def default_picon_path(self, value):
self._cp_settings["backup_local_path"] = value self._settings["default_picon_path"] = value
@property
def profile_data_path(self):
return "{}data{}{}{}".format(self.default_data_path, SEP, self._current_profile, SEP)
@profile_data_path.setter
def profile_data_path(self, value):
self._cp_settings["profile_data_path"] = value
@property
def profile_picons_path(self):
if self.profile_folder_is_default:
return "{}picons{}".format(self.profile_data_path, SEP)
return "{}{}{}".format(self.default_picon_path, self._current_profile, SEP)
@profile_picons_path.setter
def profile_picons_path(self, value):
self._cp_settings["profile_picons_path"] = value
@property
def profile_backup_path(self):
if self.profile_folder_is_default:
return "{}backup{}".format(self.profile_data_path, SEP)
return "{}{}{}".format(self.default_backup_path, self._current_profile, SEP)
@profile_backup_path.setter
def profile_backup_path(self, value):
self._cp_settings["profile_backup_path"] = value
@property @property
def records_path(self): def records_path(self):
@@ -433,7 +462,7 @@ class Settings:
@property @property
def transcoding_presets(self): def transcoding_presets(self):
return self._settings.get("transcoding_presets", get_default_transcoding_presets()) return self._settings.get("transcoding_presets", self.get_default_transcoding_presets())
@transcoding_presets.setter @transcoding_presets.setter
def transcoding_presets(self, value): def transcoding_presets(self, value):
@@ -667,7 +696,7 @@ class Settings:
@property @property
@lru_cache(1) @lru_cache(1)
def themes_path(self): def themes_path(self):
return "{}/.themes/".format(HOME_PATH) return "{}{}.themes{}".format(HOME_PATH, SEP, SEP)
@property @property
def icon_theme(self): def icon_theme(self):
@@ -680,7 +709,7 @@ class Settings:
@property @property
@lru_cache(1) @lru_cache(1)
def icon_themes_path(self): def icon_themes_path(self):
return "{}/.icons/".format(HOME_PATH) return "{}{}.icons{}".format(HOME_PATH, SEP, SEP)
@property @property
def is_darwin(self): def is_darwin(self):
@@ -725,6 +754,49 @@ class Settings:
def is_enable_experimental(self, value): def is_enable_experimental(self, value):
self._settings["enable_experimental"] = value self._settings["enable_experimental"] = value
# **************** Get-Set settings **************** #
@staticmethod
def get_settings():
if not os.path.isfile(CONFIG_FILE) or os.stat(CONFIG_FILE).st_size == 0:
Settings.write_settings(Settings.get_default_settings())
with open(CONFIG_FILE, "r", encoding="utf-8") as config_file:
return json.load(config_file)
@staticmethod
def get_default_settings(profile_name="default"):
def_settings = SettingsType.ENIGMA_2.get_default_settings()
return {
"version": Settings.__VERSION,
"default_profile": Defaults.DEFAULT_PROFILE.value,
"profiles": {profile_name: def_settings},
"v5_support": Defaults.V5_SUPPORT.value,
"http_api_support": Defaults.HTTP_API_SUPPORT.value,
"enable_yt_dl": Defaults.ENABLE_YT_DL.value,
"enable_send_to": Defaults.ENABLE_SEND_TO.value,
"use_colors": Defaults.USE_COLORS.value,
"new_color": Defaults.NEW_COLOR.value,
"extra_color": Defaults.EXTRA_COLOR.value,
"fav_click_mode": Defaults.FAV_CLICK_MODE.value,
"profile_folder_is_default": Defaults.PROFILE_FOLDER_DEFAULT.value,
"records_path": Defaults.RECORDS_PATH.value
}
@staticmethod
def get_default_transcoding_presets():
return {"720p TV/device": {"vcodec": "h264", "vb": "1500", "width": "1280", "height": "720", "acodec": "mp3",
"ab": "192", "channels": "2", "samplerate": "44100", "scodec": "none"},
"1080p TV/device": {"vcodec": "h264", "vb": "3500", "width": "1920", "height": "1080", "acodec": "mp3",
"ab": "192", "channels": "2", "samplerate": "44100", "scodec": "none"}}
@staticmethod
def write_settings(config):
os.makedirs(os.path.dirname(CONFIG_PATH), exist_ok=True)
with open(CONFIG_FILE, "w", encoding="utf-8") as config_file:
json.dump(config, config_file, indent=" ")
if __name__ == "__main__": if __name__ == "__main__":
pass pass

View File

@@ -1,3 +1,31 @@
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 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
#
""" Module for working with YouTube service """ """ Module for working with YouTube service """
import gzip import gzip
import json import json
@@ -12,6 +40,7 @@ from urllib.parse import unquote
from urllib.request import Request, urlopen, urlretrieve from urllib.request import Request, urlopen, urlretrieve
from app.commons import log from app.commons import log
from app.settings import SEP
from app.ui.uicommons import show_notification from app.ui.uicommons import show_notification
_YT_PATTERN = re.compile(r"https://www.youtube.com/.+(?:v=)([\w-]{11}).*") _YT_PATTERN = re.compile(r"https://www.youtube.com/.+(?:v=)([\w-]{11}).*")
@@ -230,7 +259,7 @@ class YouTubeDL:
"cookiefile": "cookies.txt"} # File name where cookies should be read from and dumped to. "cookiefile": "cookies.txt"} # File name where cookies should be read from and dumped to.
def __init__(self, settings, callback): def __init__(self, settings, callback):
self._path = settings.default_data_path + "tools/" self._path = "{}tools{}".format(settings.default_data_path, SEP)
self._update = settings.enable_yt_dl_update self._update = settings.enable_yt_dl_update
self._supported = {"22", "18"} self._supported = {"22", "18"}
self._dl = None self._dl = None
@@ -247,7 +276,7 @@ class YouTubeDL:
return cls._DL_INSTANCE return cls._DL_INSTANCE
def init(self): def init(self):
if not os.path.isfile(self._path + "youtube_dl/version.py"): if not os.path.isfile("{}youtube_dl{}version.py".format(self._path, SEP)):
self.get_latest_release() self.get_latest_release()
if self._path not in sys.path: if self._path not in sys.path:

View File

@@ -1,3 +1,31 @@
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 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 os import os
import shutil import shutil
import tempfile import tempfile
@@ -7,7 +35,7 @@ from datetime import datetime
from enum import Enum from enum import Enum
from app.commons import run_idle from app.commons import run_idle
from app.settings import SettingsType from app.settings import SettingsType, SEP
from app.ui.dialogs import show_dialog, DialogType, get_builder from app.ui.dialogs import show_dialog, DialogType, get_builder
from app.ui.main_helper import append_text_to_tview from app.ui.main_helper import append_text_to_tview
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, KeyboardKey, MOD_MASK
@@ -34,8 +62,8 @@ class BackupDialog:
self._settings = settings self._settings = settings
self._s_type = settings.setting_type self._s_type = settings.setting_type
self._data_path = self._settings.data_local_path self._data_path = self._settings.profile_data_path
self._backup_path = self._settings.backup_local_path or self._data_path + "backup/" self._backup_path = self._settings.profile_backup_path or "{}backup{}".format(self._data_path, os.sep)
self._open_data_callback = callback self._open_data_callback = callback
self._dialog_window = builder.get_object("dialog_window") self._dialog_window = builder.get_object("dialog_window")
self._dialog_window.set_transient_for(transient) self._dialog_window.set_transient_for(transient)
@@ -149,7 +177,7 @@ class BackupDialog:
clear_data_path(self._data_path) clear_data_path(self._data_path)
shutil.unpack_archive(full_file_name, self._data_path) shutil.unpack_archive(full_file_name, self._data_path)
elif restore_type is RestoreType.BOUQUETS: elif restore_type is RestoreType.BOUQUETS:
tmp_dir = tempfile.gettempdir() + "/" + file_name tmp_dir = tempfile.gettempdir() + SEP + file_name
cond = (".tv", ".radio") if self._s_type is SettingsType.ENIGMA_2 else "bouquets.xml" cond = (".tv", ".radio") if self._s_type is SettingsType.ENIGMA_2 else "bouquets.xml"
shutil.unpack_archive(full_file_name, tmp_dir) shutil.unpack_archive(full_file_name, tmp_dir)
for file in filter(lambda f: f.endswith(cond), os.listdir(self._data_path)): for file in filter(lambda f: f.endswith(cond), os.listdir(self._data_path)):
@@ -188,7 +216,7 @@ def backup_data(path, backup_path, move=True):
Returns full path to the compressed file. Returns full path to the compressed file.
""" """
backup_path = "{}{}/".format(backup_path, datetime.now().strftime("%Y-%m-%d_%H-%M-%S")) backup_path = "{}{}{}".format(backup_path, datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), SEP)
os.makedirs(os.path.dirname(backup_path), exist_ok=True) os.makedirs(os.path.dirname(backup_path), exist_ok=True)
os.makedirs(os.path.dirname(path), exist_ok=True) os.makedirs(os.path.dirname(path), exist_ok=True)
# backup files in data dir(skipping dirs and satellites.xml) # backup files in data dir(skipping dirs and satellites.xml)

View File

@@ -141,7 +141,7 @@ def get_file_chooser_dialog(transient, text, settings, action_type, file_filter,
if file_filter is not None: if file_filter is not None:
dialog.add_filter(file_filter) dialog.add_filter(file_filter)
dialog.set_current_folder(settings.data_local_path) dialog.set_current_folder(settings.profile_data_path)
response = dialog.run() response = dialog.run()
if response == Gtk.ResponseType.ACCEPT: if response == Gtk.ResponseType.ACCEPT:

View File

@@ -1,3 +1,31 @@
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 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 os import os
from gi.repository import GLib from gi.repository import GLib
@@ -58,7 +86,7 @@ class DownloadDialog:
def init_ui_settings(self): def init_ui_settings(self):
self._host_entry.set_text(self._settings.host) self._host_entry.set_text(self._settings.host)
self._data_path_entry.set_text(self._settings.data_local_path) self._data_path_entry.set_text(self._settings.profile_data_path)
is_enigma = self._s_type is SettingsType.ENIGMA_2 is_enigma = self._s_type is SettingsType.ENIGMA_2
self._webtv_radio_button.set_visible(not is_enigma) self._webtv_radio_button.set_visible(not is_enigma)
self._use_http_box.set_visible(is_enigma) self._use_http_box.set_visible(is_enigma)
@@ -128,9 +156,9 @@ class DownloadDialog:
try: try:
if download: if download:
if backup and d_type is not DownloadType.SATELLITES: if backup and d_type is not DownloadType.SATELLITES:
data_path = self._settings.data_local_path or self._data_path_entry.get_text() data_path = self._settings.profile_data_path or self._data_path_entry.get_text()
os.makedirs(os.path.dirname(data_path), exist_ok=True) os.makedirs(os.path.dirname(data_path), exist_ok=True)
backup_path = self._settings.backup_local_path or data_path + "backup/" backup_path = self._settings.profile_backup_path or self._settings.default_backup_path
backup_src = backup_data(data_path, backup_path, d_type is DownloadType.ALL) backup_src = backup_data(data_path, backup_path, d_type is DownloadType.ALL)
download_data(settings=self._settings, download_type=d_type, callback=self.append_output) download_data(settings=self._settings, download_type=d_type, callback=self.append_output)

View File

@@ -1,3 +1,31 @@
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 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 gzip import gzip
import locale import locale
import os import os
@@ -12,6 +40,7 @@ from gi.repository import GLib
from app.commons import run_idle, run_task from app.commons import run_idle, run_task
from app.connections import download_data, DownloadType from app.connections import download_data, DownloadType
from app.eparser.ecommons import BouquetService, BqServiceType from app.eparser.ecommons import BouquetService, BqServiceType
from app.settings import SEP
from app.tools.epg import EPG, ChannelsParser from app.tools.epg import EPG, ChannelsParser
from app.ui.dialogs import get_message, show_dialog, DialogType, get_builder from app.ui.dialogs import get_message, show_dialog, DialogType, get_builder
from .main_helper import on_popup_menu, update_entry_data from .main_helper import on_popup_menu, update_entry_data
@@ -480,7 +509,7 @@ class EpgDialog:
# ***************** Options *********************# # ***************** Options *********************#
def init_options(self): def init_options(self):
epg_dat_path = self._settings.data_local_path + "epg/" epg_dat_path = "{}epg{}".format(self._settings.profile_data_path, SEP)
self._epg_dat_path_entry.set_text(epg_dat_path) self._epg_dat_path_entry.set_text(epg_dat_path)
default_epg_data_stb_path = "/etc/enigma2" default_epg_data_stb_path = "/etc/enigma2"
epg_options = self._settings.epg_options epg_options = self._settings.epg_options

View File

@@ -1,3 +1,31 @@
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 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
#
""" Simple FTP client module. """ """ Simple FTP client module. """
import subprocess import subprocess
from collections import namedtuple from collections import namedtuple
@@ -140,7 +168,7 @@ class FtpClientBox(Gtk.HBox):
@run_task @run_task
def init_file_data(self, path=None): def init_file_data(self, path=None):
self.append_file_data(Path(path if path else self._settings.data_local_path)) self.append_file_data(Path(path if path else self._settings.profile_data_path))
@run_idle @run_idle
def append_file_data(self, path: Path): def append_file_data(self, path: Path):

View File

@@ -618,7 +618,7 @@ class M3uImportDialog(IptvListDialog):
self._app = app self._app = app
self._picons = app._picons self._picons = app._picons
self._pic_path = app._settings.picons_local_path self._pic_path = app._settings.profile_picons_path
self._services = None self._services = None
self._url_count = 0 self._url_count = 0
self._errors_count = 0 self._errors_count = 0

View File

@@ -632,7 +632,7 @@ class Application(Gtk.Application):
@run_idle @run_idle
def update_picons_size(self): def update_picons_size(self):
self._picons_size = self._settings.list_picon_size self._picons_size = self._settings.list_picon_size
update_picons_data(self._settings.picons_local_path, self._picons, self._picons_size) update_picons_data(self._settings.profile_picons_path, self._picons, self._picons_size)
self._fav_model.foreach(lambda m, p, itr: m.set_value(itr, Column.FAV_PICON, self._picons.get( self._fav_model.foreach(lambda m, p, itr: m.set_value(itr, Column.FAV_PICON, self._picons.get(
self._services.get(m.get_value(itr, Column.FAV_ID)).picon_id, None))) self._services.get(m.get_value(itr, Column.FAV_ID)).picon_id, None)))
self._services_model.foreach(lambda m, p, itr: m.set_value(itr, Column.SRV_PICON, self._picons.get( self._services_model.foreach(lambda m, p, itr: m.set_value(itr, Column.SRV_PICON, self._picons.get(
@@ -1146,7 +1146,7 @@ class Application(Gtk.Application):
target_column = Column.FAV_ID if target is ViewTarget.FAV else Column.SRV_FAV_ID target_column = Column.FAV_ID if target is ViewTarget.FAV else Column.SRV_FAV_ID
srv = self._services.get(model[path][target_column], None) srv = self._services.get(model[path][target_column], None)
if srv and srv.picon_id: if srv and srv.picon_id:
tooltip.set_icon(get_picon_pixbuf(self._settings.picons_local_path + srv.picon_id, tooltip.set_icon(get_picon_pixbuf(self._settings.profile_picons_path + srv.picon_id,
size=self._settings.tooltip_logo_size)) size=self._settings.tooltip_logo_size))
tooltip.set_text( tooltip.set_text(
self.get_hint_for_bq_list(srv) if target is ViewTarget.FAV else self.get_hint_for_srv_list(srv)) self.get_hint_for_bq_list(srv) if target is ViewTarget.FAV else self.get_hint_for_srv_list(srv))
@@ -1588,8 +1588,8 @@ class Application(Gtk.Application):
if current_profile != self._settings.current_profile: if current_profile != self._settings.current_profile:
self.init_profiles(self._settings.current_profile) self.init_profiles(self._settings.current_profile)
data_path = self._settings.data_local_path if data_path is None else data_path data_path = self._settings.profile_data_path if data_path is None else data_path
local_path = self._settings.data_local_path local_path = self._settings.profile_data_path
os.makedirs(os.path.dirname(local_path), exist_ok=True) os.makedirs(os.path.dirname(local_path), exist_ok=True)
if data_path != local_path: if data_path != local_path:
@@ -1606,7 +1606,7 @@ class Application(Gtk.Application):
yield True yield True
services = get_services(data_path, prf, self.get_format_version() if prf is SettingsType.ENIGMA_2 else 0) services = get_services(data_path, prf, self.get_format_version() if prf is SettingsType.ENIGMA_2 else 0)
yield True yield True
update_picons_data(self._settings.picons_local_path, self._picons, self._picons_size) update_picons_data(self._settings.profile_picons_path, self._picons, self._picons_size)
yield True yield True
except FileNotFoundError as e: except FileNotFoundError as e:
msg = get_message("Please, download files from receiver or setup your path for read data!") msg = get_message("Please, download files from receiver or setup your path for read data!")
@@ -1820,8 +1820,8 @@ class Application(Gtk.Application):
def save_data(self, callback=None, ext_path=None): def save_data(self, callback=None, ext_path=None):
self._save_tool_button.set_sensitive(False) self._save_tool_button.set_sensitive(False)
profile = self._s_type profile = self._s_type
path = ext_path or self._settings.data_local_path path = ext_path or self._settings.profile_data_path
backup_path = self._settings.backup_local_path backup_path = self._settings.profile_backup_path
# Backup data or clearing data path # Backup data or clearing data path
backup_data(path, backup_path) if not ext_path and self._settings.backup_before_save else clear_data_path(path) backup_data(path, backup_path) if not ext_path and self._settings.backup_before_save else clear_data_path(path)
yield True yield True
@@ -3290,7 +3290,7 @@ class Application(Gtk.Application):
@run_task @run_task
def update_picons(self): def update_picons(self):
update_picons_data(self._settings.picons_local_path, self._picons, self._picons_size) update_picons_data(self._settings.profile_picons_path, self._picons, self._picons_size)
append_picons(self._picons, self._services_model) append_picons(self._picons, self._services_model)
def on_assign_picon(self, view, src_path=None, dst_path=None): def on_assign_picon(self, view, src_path=None, dst_path=None):

View File

@@ -1,3 +1,31 @@
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 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
#
""" Helper module for the ui. """ """ Helper module for the ui. """
import os import os
import shutil import shutil
@@ -9,7 +37,7 @@ from gi.repository import GdkPixbuf, GLib
from app.eparser import Service from app.eparser import Service
from app.eparser.ecommons import Flag, BouquetService, Bouquet, BqType from app.eparser.ecommons import Flag, BouquetService, Bouquet, BqType
from app.eparser.enigma.bouquets import BqServiceType, to_bouquet_id from app.eparser.enigma.bouquets import BqServiceType, to_bouquet_id
from app.settings import SettingsType from app.settings import SettingsType, SEP
from .dialogs import show_dialog, DialogType, get_chooser_dialog from .dialogs import show_dialog, DialogType, get_chooser_dialog
from .uicommons import ViewTarget, BqGenType, Gtk, Gdk, HIDE_ICON, LOCKED_ICON, KeyboardKey, Column from .uicommons import ViewTarget, BqGenType, Gtk, Gdk, HIDE_ICON, LOCKED_ICON, KeyboardKey, Column
@@ -402,7 +430,7 @@ def assign_picons(target, srv_view, fav_view, transient, picons, settings, servi
picon_id = services.get(fav_id)[Column.SRV_PICON_ID] picon_id = services.get(fav_id)[Column.SRV_PICON_ID]
if picon_id: if picon_id:
picons_path = dst_path or settings.picons_local_path picons_path = dst_path or settings.profile_picons_path
os.makedirs(os.path.dirname(picons_path), exist_ok=True) os.makedirs(os.path.dirname(picons_path), exist_ok=True)
picon_file = picons_path + picon_id picon_file = picons_path + picon_id
try: try:
@@ -499,8 +527,8 @@ def remove_all_unused_picons(settings, picons, services):
def remove_picons(settings, picon_ids, picons): def remove_picons(settings, picon_ids, picons):
pions_path = settings.picons_local_path pions_path = settings.profile_picons_path
backup_path = settings.backup_local_path + "picons/" backup_path = "{}{}{}".format(settings.profile_backup_path, "picons", SEP)
os.makedirs(os.path.dirname(backup_path), exist_ok=True) os.makedirs(os.path.dirname(backup_path), exist_ok=True)
for p_id in picon_ids: for p_id in picon_ids:
picons[p_id] = None picons[p_id] = None

View File

@@ -37,7 +37,7 @@ from gi.repository import GLib, GdkPixbuf, Gio
from app.commons import run_idle, run_task, run_with_delay from app.commons import run_idle, run_task, run_with_delay
from app.connections import upload_data, DownloadType, download_data, remove_picons from app.connections import upload_data, DownloadType, download_data, remove_picons
from app.settings import SettingsType, Settings from app.settings import SettingsType, Settings, SEP
from app.tools.picons import (PiconsParser, parse_providers, Provider, convert_to, download_picon, PiconsCzDownloader, from app.tools.picons import (PiconsParser, parse_providers, Provider, convert_to, download_picon, PiconsCzDownloader,
PiconsError) PiconsError)
from app.tools.satellites import SatellitesParser, SatelliteSource from app.tools.satellites import SatellitesParser, SatelliteSource
@@ -184,7 +184,7 @@ class PiconManager(Gtk.Box):
# Settings # Settings
self._settings = settings self._settings = settings
self._s_type = settings.setting_type self._s_type = settings.setting_type
self._picons_dir_entry.set_text(self._settings.picons_local_path) self._picons_dir_entry.set_text(self._settings.profile_picons_path)
self.pack_start(builder.get_object("picon_manager_frame"), True, True, 0) self.pack_start(builder.get_object("picon_manager_frame"), True, True, 0)
self.show() self.show()
@@ -197,7 +197,7 @@ class PiconManager(Gtk.Box):
def on_picons_dest_view_realize(self, view): def on_picons_dest_view_realize(self, view):
self._services = {s.picon_id: s for s in self._app.current_services.values() if s.picon_id} self._services = {s.picon_id: s for s in self._app.current_services.values() if s.picon_id}
self._explorer_dest_path_button.select_filename(self._settings.picons_local_path) self._explorer_dest_path_button.select_filename(self._settings.profile_picons_path)
def on_picons_src_changed(self, button): def on_picons_src_changed(self, button):
self.update_picons_data(self._picons_src_view, button) self.update_picons_data(self._picons_src_view, button)
@@ -234,7 +234,7 @@ class PiconManager(Gtk.Box):
if self._terminate: if self._terminate:
return return
p_path = "{}/{}".format(path, file) p_path = "{}{}{}".format(path, SEP, file)
p = self.get_pixbuf_at_scale(p_path, 72, 48, True) p = self.get_pixbuf_at_scale(p_path, 72, 48, True)
if p: if p:
yield model.append((p, file, p_path)) yield model.append((p, file, p_path))
@@ -327,7 +327,7 @@ class PiconManager(Gtk.Box):
c_id = Column.SRV_FAV_ID c_id = Column.SRV_FAV_ID
t_mod = target_view.get_model() t_mod = target_view.get_model()
dest_path = self._explorer_dest_path_button.get_filename() + "/" dest_path = self._explorer_dest_path_button.get_filename() + SEP
self.update_picons_dest_view(self._app.on_assign_picon(target_view, model[path][-1], dest_path)) self.update_picons_dest_view(self._app.on_assign_picon(target_view, model[path][-1], dest_path))
self.show_assign_info([t_mod.get_value(t_mod.get_iter_from_string(itr), c_id) for itr in itr_str.split(",")]) self.show_assign_info([t_mod.get_value(t_mod.get_iter_from_string(itr), c_id) for itr in itr_str.split(",")])
@@ -371,7 +371,7 @@ class PiconManager(Gtk.Box):
if len(uris) == 2: if len(uris) == 2:
name, fav_id = self._current_picon_info name, fav_id = self._current_picon_info
src = urlparse(unquote(uris[0])).path src = urlparse(unquote(uris[0])).path
dst = "{}/{}".format(urlparse(unquote(uris[1])).path, name) dst = "{}{}{}".format(urlparse(unquote(uris[1])).path, SEP, name)
if src != dst: if src != dst:
shutil.copy(src, dst) shutil.copy(src, dst)
for row in get_base_model(self._picons_dest_view.get_model()): for row in get_base_model(self._picons_dest_view.get_model()):
@@ -443,7 +443,7 @@ class PiconManager(Gtk.Box):
return return
settings = Settings(self._settings.settings) settings = Settings(self._settings.settings)
settings.picons_local_path = "{}/".format(dest_path) settings.profile_picons_path = "{}{}".format(dest_path, SEP)
self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO) self.show_info_message(get_message("Please, wait..."), Gtk.MessageType.INFO)
self.run_func(lambda: upload_data(settings=settings, self.run_func(lambda: upload_data(settings=settings,
download_type=DownloadType.PICONS, download_type=DownloadType.PICONS,
@@ -458,7 +458,7 @@ class PiconManager(Gtk.Box):
return return
settings = Settings(self._settings.settings) settings = Settings(self._settings.settings)
settings.picons_local_path = path + "/" settings.profile_picons_path = path + SEP
self.run_func(lambda: download_data(settings=settings, self.run_func(lambda: download_data(settings=settings,
download_type=DownloadType.PICONS, download_type=DownloadType.PICONS,
callback=self.append_output, callback=self.append_output,

View File

@@ -52,7 +52,7 @@ class SatellitesTool(Gtk.Box):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._app = app self._app = app
self._data_path = settings.data_local_path + "satellites.xml" self._data_path = settings.profile_data_path + "satellites.xml"
self._settings = settings self._settings = settings
handlers = {"on_remove": self.on_remove, handlers = {"on_remove": self.on_remove,

View File

@@ -81,8 +81,8 @@ class ServiceDetailsDialog:
self._dialog.set_transient_for(transient) self._dialog.set_transient_for(transient)
self._s_type = settings.setting_type self._s_type = settings.setting_type
self._tr_type = TrType.Satellite self._tr_type = TrType.Satellite
self._satellites_xml_path = settings.data_local_path + "satellites.xml" self._satellites_xml_path = settings.profile_data_path + "satellites.xml"
self._picons_dir_path = settings.picons_local_path self._picons_path = settings.profile_picons_path
self._services_view = srv_view self._services_view = srv_view
self._fav_view = fav_view self._fav_view = fav_view
self._action = action self._action = action
@@ -504,13 +504,13 @@ class ServiceDetailsDialog:
Column.FAV_PICON: new_service.picon}) Column.FAV_PICON: new_service.picon})
def update_picon_name(self, old_name, new_name): def update_picon_name(self, old_name, new_name):
if not os.path.isdir(self._picons_dir_path): if not os.path.isdir(self._picons_path):
return return
for file_name in os.listdir(self._picons_dir_path): for file_name in os.listdir(self._picons_path):
if file_name == old_name: if file_name == old_name:
old_file = os.path.join(self._picons_dir_path, old_name) old_file = os.path.join(self._picons_path, old_name)
new_file = os.path.join(self._picons_dir_path, new_name) new_file = os.path.join(self._picons_path, new_name)
os.rename(old_file, new_file) os.rename(old_file, new_file)
break break

View File

@@ -1199,7 +1199,7 @@ Author: Dmitriy Yefremov
<property name="row_spacing">5</property> <property name="row_spacing">5</property>
<property name="column_spacing">10</property> <property name="column_spacing">10</property>
<child> <child>
<object class="GtkLabel" id="data_dir_label"> <object class="GtkLabel" id="data_path_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
@@ -1212,7 +1212,7 @@ Author: Dmitriy Yefremov
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="picons_dir_label"> <object class="GtkLabel" id="picons_path_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
@@ -1224,7 +1224,7 @@ Author: Dmitriy Yefremov
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="backup_dir_label"> <object class="GtkLabel" id="backup_path_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
@@ -1236,7 +1236,7 @@ Author: Dmitriy Yefremov
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="data_dir_field"> <object class="GtkEntry" id="data_path_field">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
@@ -1253,7 +1253,7 @@ Author: Dmitriy Yefremov
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="picons_dir_field"> <object class="GtkEntry" id="picons_path_field">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
@@ -1270,7 +1270,7 @@ Author: Dmitriy Yefremov
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="backup_dir_field"> <object class="GtkEntry" id="backup_path_field">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
@@ -1327,7 +1327,7 @@ Author: Dmitriy Yefremov
<property name="margin_bottom">5</property> <property name="margin_bottom">5</property>
<property name="column_spacing">10</property> <property name="column_spacing">10</property>
<child> <child>
<object class="GtkLabel" id="record_dir_label"> <object class="GtkLabel" id="record_path_label">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="label" translatable="yes">Streams record path:</property> <property name="label" translatable="yes">Streams record path:</property>
@@ -1339,7 +1339,7 @@ Author: Dmitriy Yefremov
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry" id="record_data_dir_field"> <object class="GtkEntry" id="record_data_path_field">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>

View File

@@ -1,9 +1,37 @@
# -*- coding: utf-8 -*-
#
# The MIT License (MIT)
#
# Copyright (c) 2018-2021 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 os import os
import re import re
from app.commons import run_task, run_idle, log from app.commons import run_task, run_idle, log
from app.connections import test_telnet, test_ftp, TestException, test_http, HttpApiException from app.connections import test_telnet, test_ftp, TestException, test_http, HttpApiException
from app.settings import SettingsType, Settings, PlayStreamsMode, IS_LINUX from app.settings import SettingsType, Settings, PlayStreamsMode, IS_LINUX, SEP
from app.ui.dialogs import show_dialog, DialogType, get_message, get_chooser_dialog, get_builder 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 .main_helper import update_entry_data, scroll_to, get_picon_pixbuf
from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT from .uicommons import Gtk, Gdk, UI_RESOURCES_PATH, FavClickMode, DEFAULT_ICON, APP_FONT
@@ -78,6 +106,7 @@ class SettingsDialog:
self._http_use_ssl_check_button = builder.get_object("http_use_ssl_check_button") self._http_use_ssl_check_button = builder.get_object("http_use_ssl_check_button")
self._telnet_port_field = builder.get_object("telnet_port_field") self._telnet_port_field = builder.get_object("telnet_port_field")
self._telnet_timeout_spin_button = builder.get_object("telnet_timeout_spin_button") self._telnet_timeout_spin_button = builder.get_object("telnet_timeout_spin_button")
self._reset_button = builder.get_object("reset_button")
# Test. # Test.
self._ftp_radio_button = builder.get_object("ftp_radio_button") self._ftp_radio_button = builder.get_object("ftp_radio_button")
self._http_radio_button = builder.get_object("http_radio_button") self._http_radio_button = builder.get_object("http_radio_button")
@@ -87,12 +116,13 @@ class SettingsDialog:
self._satellites_xml_field = builder.get_object("satellites_xml_field") self._satellites_xml_field = builder.get_object("satellites_xml_field")
self._picons_paths_box = builder.get_object("picons_paths_box") self._picons_paths_box = builder.get_object("picons_paths_box")
# Paths. # Paths.
self._picons_dir_field = builder.get_object("picons_dir_field") self._picons_path_field = builder.get_object("picons_path_field")
self._data_dir_field = builder.get_object("data_dir_field") self._data_path_field = builder.get_object("data_path_field")
self._backup_dir_field = builder.get_object("backup_dir_field") self._backup_path_field = builder.get_object("backup_path_field")
self._default_data_dir_field = builder.get_object("default_data_dir_field") self._record_data_path_field = builder.get_object("record_data_path_field")
self._record_data_dir_field = builder.get_object("record_data_dir_field")
self._default_data_paths_switch = builder.get_object("default_data_paths_switch") self._default_data_paths_switch = builder.get_object("default_data_paths_switch")
self._default_data_paths_switch.bind_property("active", self._backup_path_field, "sensitive", 4)
self._default_data_paths_switch.bind_property("active", self._picons_path_field, "sensitive", 4)
# Info bar. # Info bar.
self._info_bar = builder.get_object("info_bar") self._info_bar = builder.get_object("info_bar")
self._message_label = builder.get_object("info_bar_message_label") self._message_label = builder.get_object("info_bar_message_label")
@@ -268,10 +298,10 @@ class SettingsDialog:
self._user_bouquet_field.set_text(self._settings.user_bouquet_path) self._user_bouquet_field.set_text(self._settings.user_bouquet_path)
self._satellites_xml_field.set_text(self._settings.satellites_xml_path) self._satellites_xml_field.set_text(self._settings.satellites_xml_path)
self._picons_paths_box.set_active_id(self._settings.picons_path) self._picons_paths_box.set_active_id(self._settings.picons_path)
self._data_dir_field.set_text(self._settings.data_local_path) self._data_path_field.set_text(self._settings.default_data_path)
self._picons_dir_field.set_text(self._settings.picons_local_path) self._picons_path_field.set_text(self._settings.default_picon_path)
self._backup_dir_field.set_text(self._settings.backup_local_path) self._backup_path_field.set_text(self._settings.default_backup_path)
self._record_data_dir_field.set_text(self._settings.records_path) self._record_data_path_field.set_text(self._settings.records_path)
self._before_save_switch.set_active(self._settings.backup_before_save) self._before_save_switch.set_active(self._settings.backup_before_save)
self._before_downloading_switch.set_active(self._settings.backup_before_downloading) self._before_downloading_switch.set_active(self._settings.backup_before_downloading)
self.set_fav_click_mode(self._settings.fav_click_mode) self.set_fav_click_mode(self._settings.fav_click_mode)
@@ -328,9 +358,6 @@ class SettingsDialog:
self._settings.user_bouquet_path = self._user_bouquet_field.get_text() self._settings.user_bouquet_path = self._user_bouquet_field.get_text()
self._settings.satellites_xml_path = self._satellites_xml_field.get_text() self._settings.satellites_xml_path = self._satellites_xml_field.get_text()
self._settings.picons_path = self._picons_paths_box.get_active_id() self._settings.picons_path = self._picons_paths_box.get_active_id()
self._settings.data_local_path = self._data_dir_field.get_text()
self._settings.picons_local_path = self._picons_dir_field.get_text()
self._settings.backup_local_path = self._backup_dir_field.get_text()
def apply_settings(self, item=None): def apply_settings(self, item=None):
if show_dialog(DialogType.QUESTION, self._dialog) != Gtk.ResponseType.OK: if show_dialog(DialogType.QUESTION, self._dialog) != Gtk.ResponseType.OK:
@@ -348,7 +375,10 @@ class SettingsDialog:
self._ext_settings.show_bq_hints = self._bouquet_hints_switch.get_active() self._ext_settings.show_bq_hints = self._bouquet_hints_switch.get_active()
self._ext_settings.show_srv_hints = self._services_hints_switch.get_active() self._ext_settings.show_srv_hints = self._services_hints_switch.get_active()
self._ext_settings.profile_folder_is_default = self._default_data_paths_switch.get_active() self._ext_settings.profile_folder_is_default = self._default_data_paths_switch.get_active()
self._ext_settings.records_path = self._record_data_dir_field.get_text() self._ext_settings.default_data_path = self._data_path_field.get_text()
self._ext_settings.default_backup_path = self._backup_path_field.get_text()
self._ext_settings.default_picon_path = self._picons_path_field.get_text()
self._ext_settings.records_path = self._record_data_path_field.get_text()
self._ext_settings.activate_transcoding = self._transcoding_switch.get_active() self._ext_settings.activate_transcoding = self._transcoding_switch.get_active()
self._ext_settings.active_preset = self._presets_combo_box.get_active_id() self._ext_settings.active_preset = self._presets_combo_box.get_active_id()
self._ext_settings.list_picon_size = int(self._picons_size_button.get_active_id()) self._ext_settings.list_picon_size = int(self._picons_size_button.get_active_id())
@@ -515,29 +545,8 @@ class SettingsDialog:
if p_settings: if p_settings:
row[0] = new_value row[0] = new_value
self._profiles[new_value] = p_settings self._profiles[new_value] = p_settings
self.update_local_paths(new_value, old_name)
self.on_profile_selected(self._profile_view, False) self.on_profile_selected(self._profile_view, False)
def update_local_paths(self, p_name, old_name, force_rename=False):
data_path = self._settings.data_local_path
picons_path = self._settings.picons_local_path
backup_path = self._settings.backup_local_path
self._settings.data_local_path = p_name.join(data_path.rsplit(old_name, 1))
self._settings.picons_local_path = p_name.join(picons_path.rsplit(old_name, 1))
self._settings.backup_local_path = p_name.join(backup_path.rsplit(old_name, 1))
if force_rename:
try:
if os.path.isdir(picons_path):
os.rename(picons_path, self._settings.picons_local_path)
if os.path.isdir(data_path):
os.rename(data_path, self._settings.data_local_path)
if os.path.isdir(backup_path):
os.rename(backup_path, self._settings.backup_local_path)
except OSError as e:
self.show_info_message(str(e), Gtk.MessageType.ERROR)
def on_profile_selected(self, view, force=True): def on_profile_selected(self, view, force=True):
if force: if force:
self.on_apply_profile_settings() self.on_apply_profile_settings()
@@ -566,6 +575,7 @@ class SettingsDialog:
def on_main_settings_visible(self, stack, param): def on_main_settings_visible(self, stack, param):
name = stack.get_visible_child_name() name = stack.get_visible_child_name()
self._apply_presets_button.set_visible(name == "streaming") self._apply_presets_button.set_visible(name == "streaming")
self._reset_button.set_visible(name == "profiles")
def on_http_use_ssl_toggled(self, button): def on_http_use_ssl_toggled(self, button):
active = button.get_active() active = button.get_active()
@@ -706,7 +716,7 @@ class SettingsDialog:
@run_idle @run_idle
def set_theme_thumbnail_image(self, theme_name): def set_theme_thumbnail_image(self, theme_name):
img_path = "{}{}/gtk-3.0/thumbnail.png".format(self._ext_settings.themes_path, theme_name) img_path = "{}{}{}gtk-3.0{}thumbnail.png".format(self._ext_settings.themes_path, theme_name, SEP, SEP)
self._theme_thumbnail_image.set_from_pixbuf(get_picon_pixbuf(img_path, 96)) self._theme_thumbnail_image.set_from_pixbuf(get_picon_pixbuf(img_path, 96))
def on_theme_add(self, button): def on_theme_add(self, button):