profiles.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. ########################################################################
  2. # Searx-Qt - Lightweight desktop application for Searx.
  3. # Copyright (C) 2020-2022 CYBERDEViL
  4. #
  5. # This file is part of Searx-Qt.
  6. #
  7. # Searx-Qt is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # Searx-Qt is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. #
  20. ########################################################################
  21. from PyQt5.QtCore import QSettings, QStandardPaths, QFile
  22. from searxqt.models.instances import InstancesModelTypes
  23. from searxqt import PROFILES_PATH, SETTINGS_PATH
  24. from searxqt.core import log
  25. from searxqt.version import __version__
  26. class ProfileItem:
  27. def __init__(
  28. self,
  29. id_='',
  30. name='',
  31. type_=InstancesModelTypes.NotDefined,
  32. preset=None
  33. ):
  34. """
  35. @param id_: Unique profile id
  36. @type id: str
  37. @param name: Profile name
  38. @type name: str
  39. @param type_: Profile type
  40. @type type_: InstancesModelTypes.*
  41. @param preset: This is only used for new profiles, it is the key of the
  42. defaults preset that should be loaded.
  43. @type preset: None or str
  44. """
  45. self.id = id_
  46. self.name = name
  47. self.type = type_
  48. self.preset = preset
  49. def serialize(self):
  50. return {
  51. 'id': self.id,
  52. 'name': self.name,
  53. 'type': self.type
  54. }
  55. def deserialize(self, data):
  56. self.id = data['id']
  57. self.name = data['name']
  58. self.type = data['type']
  59. class Profiles:
  60. def __init__(self):
  61. self._currentProfile = ProfileItem()
  62. self._profiles = {
  63. # list with searxqt.models.profiles.ProfileItem's
  64. 'profiles': [],
  65. # str: profile id
  66. 'default': None
  67. }
  68. def __contains__(self, profile):
  69. return bool(profile in self._profiles['profiles'])
  70. def __len__(self):
  71. return len(self._profiles['profiles'])
  72. def __getitem__(self, index):
  73. return self._profiles['profiles'][index]
  74. def add(self, profile):
  75. """ Add profile to this object. This will not save profiles.conf!
  76. @param profile: ProfileItem to add.
  77. @type profile: ProfileItem
  78. """
  79. self._profiles['profiles'].append(profile)
  80. def remove(self, profile):
  81. """ Remove profile from this object. This will not save profiles.conf!
  82. @param profile: ProfileItem to remove.
  83. @type profile: ProfileItem
  84. """
  85. self._profiles['profiles'].remove(profile)
  86. def current(self):
  87. """ Returns current profile
  88. @rtype: ProfileItem
  89. """
  90. return self._currentProfile
  91. def default(self):
  92. """ Returns the default profile
  93. @rtype: ProfileItem or None when not set.
  94. """
  95. return self._profiles['default']
  96. def setDefault(self, profile):
  97. """
  98. @param profile: ProfileItem to set as default.
  99. @type profile: ProfileItem or None to unset.
  100. """
  101. self._profiles['default'] = profile
  102. def loadProfiles(self, settings):
  103. """ Load profiles.conf
  104. """
  105. self._profiles['default'] = None
  106. default = settings.value('default', '', str)
  107. if default != '':
  108. self._profiles['default'] = ProfileItem().deserialize(default)
  109. # Deserialize profiles
  110. self._profiles['profiles'].clear()
  111. for profile in settings.value('profiles', list(), list):
  112. item = ProfileItem(profile['id'], profile['name'], profile['type'])
  113. self._profiles['profiles'].append(item)
  114. def saveProfiles(self):
  115. settings = self.settings()
  116. if self._profiles['default'] is not None:
  117. settings.setValue('default', self._profiles['default'].serialize())
  118. # Store searx-qt version (for backward compatibility)
  119. settings.setValue('version', __version__)
  120. # Serialize profiles
  121. profiles = []
  122. for profile in self._profiles['profiles']:
  123. profiles.append(profile.serialize())
  124. settings.setValue('profiles', profiles)
  125. def getActiveProfiles(self, settings):
  126. """ Returns a list with active profile id's
  127. """
  128. return settings.value('active', list(), list)
  129. def profileActive(self, profile):
  130. """ Check if profile is active or not. This will re-read the
  131. profiles.conf file.
  132. @rtype: bool
  133. """
  134. settings = self.settings()
  135. return bool(profile.id in self.getActiveProfiles(settings))
  136. def profileExists(self, profile):
  137. """ Check if profile is still present in profiles.conf. This will
  138. re-read the profiles.conf file. It however won't store the read
  139. data in this object.
  140. @rtype: bool
  141. """
  142. settings = self.settings()
  143. profiles = settings.value('profiles', list(), list)
  144. for p in profiles:
  145. if p['id'] == profile.id:
  146. return True
  147. return False
  148. def settings(self):
  149. return QSettings(SETTINGS_PATH, 'profiles')
  150. def releaseAll(self):
  151. """ Release active profiles. This may be wanted after a crash of
  152. searx-qt or the system. User must make sure to close all other
  153. instances of searx-qt first.
  154. """
  155. settings = self.settings()
  156. settings.remove('active')
  157. def setProfile(self, settings, profile):
  158. """ Sets current profile active (also stores it to profiles.conf).
  159. @type settings: QSettings
  160. @type profile: ProfileItem
  161. """
  162. # Read a list of active profile id's
  163. activeProfiles = self.getActiveProfiles(settings)
  164. if self._currentProfile.id in activeProfiles:
  165. # Remove old profile id from active profiles
  166. activeProfiles.remove(self._currentProfile.id)
  167. # Append current profile id to the active profiles list
  168. if profile.id:
  169. activeProfiles.append(profile.id)
  170. # Store the changes
  171. settings.setValue('active', activeProfiles)
  172. self._currentProfile = profile
  173. def removeProfileFiles(self, profileIds):
  174. """ Removes all profile files for given profile ID's.
  175. @param profileIds: List with unique profile ID's
  176. @type profileIds: list
  177. """
  178. for profileId in profileIds:
  179. self.removeProfileFile(profileId)
  180. def removeProfileFile(self, profileId):
  181. """ Remove profile file for given profile ID.
  182. @param profileId: Unique profile id
  183. @type profileId: str
  184. """
  185. # Locate full file-path
  186. confFilePath = QStandardPaths.locate(
  187. QStandardPaths.ConfigLocation,
  188. f"{PROFILES_PATH}{profileId}.conf",
  189. QStandardPaths.LocateFile
  190. )
  191. # confFilePath may be a empty string, this means the file wasn't
  192. # found. This can happen when the profile has never loaded and saved.
  193. if confFilePath:
  194. log.debug(f"Trying to remove: {confFilePath}", self)
  195. confFile = QFile(confFilePath)
  196. if confFile.remove():
  197. log.debug(f"Removed {confFilePath}", self)
  198. return True
  199. else:
  200. log.error(f"Could not remove {confFilePath}", self)
  201. return False
  202. def names(self):
  203. """ Returns all profile names in a list.
  204. @rtype: list
  205. """
  206. return [profile.name for profile in self._profiles['profiles']]