profiles.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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 = int(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()
  109. self._profiles['default'].deserialize(default)
  110. # Deserialize profiles
  111. self._profiles['profiles'].clear()
  112. for profile in settings.value('profiles', list(), list):
  113. item = ProfileItem(profile['id'], profile['name'], profile['type'])
  114. self._profiles['profiles'].append(item)
  115. def saveProfiles(self):
  116. settings = self.settings()
  117. if self._profiles['default'] is not None:
  118. settings.setValue('default', self._profiles['default'].serialize())
  119. else:
  120. settings.setValue('default', '')
  121. # Store searx-qt version (for backward compatibility)
  122. settings.setValue('version', __version__)
  123. # Serialize profiles
  124. profiles = []
  125. for profile in self._profiles['profiles']:
  126. profiles.append(profile.serialize())
  127. settings.setValue('profiles', profiles)
  128. def getActiveProfiles(self, settings):
  129. """ Returns a list with active profile id's
  130. """
  131. return settings.value('active', list(), list)
  132. def profileActive(self, profile):
  133. """ Check if profile is active or not. This will re-read the
  134. profiles.conf file.
  135. @rtype: bool
  136. """
  137. settings = self.settings()
  138. return bool(profile.id in self.getActiveProfiles(settings))
  139. def profileExists(self, profile):
  140. """ Check if profile is still present in profiles.conf. This will
  141. re-read the profiles.conf file. It however won't store the read
  142. data in this object.
  143. @rtype: bool
  144. """
  145. settings = self.settings()
  146. profiles = settings.value('profiles', list(), list)
  147. for p in profiles:
  148. if p['id'] == profile.id:
  149. return True
  150. return False
  151. def settings(self):
  152. return QSettings(SETTINGS_PATH, 'profiles')
  153. def releaseAll(self):
  154. """ Release active profiles. This may be wanted after a crash of
  155. searx-qt or the system. User must make sure to close all other
  156. instances of searx-qt first.
  157. """
  158. settings = self.settings()
  159. settings.remove('active')
  160. def setProfile(self, settings, profile):
  161. """ Sets current profile active (also stores it to profiles.conf).
  162. @type settings: QSettings
  163. @type profile: ProfileItem
  164. """
  165. # Read a list of active profile id's
  166. activeProfiles = self.getActiveProfiles(settings)
  167. if self._currentProfile.id in activeProfiles:
  168. # Remove old profile id from active profiles
  169. activeProfiles.remove(self._currentProfile.id)
  170. # Append current profile id to the active profiles list
  171. if profile.id:
  172. activeProfiles.append(profile.id)
  173. # Store the changes
  174. settings.setValue('active', activeProfiles)
  175. self._currentProfile = profile
  176. def removeProfileFiles(self, profileIds):
  177. """ Removes all profile files for given profile ID's.
  178. @param profileIds: List with unique profile ID's
  179. @type profileIds: list
  180. """
  181. for profileId in profileIds:
  182. self.removeProfileFile(profileId)
  183. def removeProfileFile(self, profileId):
  184. """ Remove profile file for given profile ID.
  185. @param profileId: Unique profile id
  186. @type profileId: str
  187. """
  188. # Locate full file-path
  189. confFilePath = QStandardPaths.locate(
  190. QStandardPaths.ConfigLocation,
  191. f"{PROFILES_PATH}{profileId}.conf",
  192. QStandardPaths.LocateFile
  193. )
  194. # confFilePath may be a empty string, this means the file wasn't
  195. # found. This can happen when the profile has never loaded and saved.
  196. if confFilePath:
  197. log.debug(f"Trying to remove: {confFilePath}", self)
  198. confFile = QFile(confFilePath)
  199. if confFile.remove():
  200. log.debug(f"Removed {confFilePath}", self)
  201. return True
  202. else:
  203. log.error(f"Could not remove {confFilePath}", self)
  204. return False
  205. def names(self):
  206. """ Returns all profile names in a list.
  207. @rtype: list
  208. """
  209. return [profile.name for profile in self._profiles['profiles']]