configuration.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. # This file is part of stov, written by Helmut Pozimski 2012-2021.
  2. #
  3. # stov is free software: you can redistribute it and/or modify
  4. # it under the terms of the GNU General Public License as published by
  5. # the Free Software Foundation, version 2 of the License.
  6. #
  7. # stov is distributed in the hope that it will be useful,
  8. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. # GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License
  13. # along with stov. If not, see <http://www.gnu.org/licenses/>.
  14. # -*- coding: utf8 -*-
  15. """This file takes care of reading, storing and updating the configuration of
  16. stov, the configuration file is expected to be in json format and reside in
  17. ~/.stov/stov.json.
  18. """
  19. import json
  20. import logging
  21. import os
  22. from lib_stov import stov_exceptions
  23. LOGGER = logging.getLogger("stov")
  24. class Conf:
  25. """This class is used to create the object which is responsible for all
  26. configuration operations.
  27. """
  28. _instance = None
  29. def __init__(self):
  30. """Constructor
  31. Constructs the conf object with some reasonable default values which
  32. should work on most systems, existence of a local mail server is
  33. assumed.
  34. """
  35. self.values = {
  36. "database": "stov.sqlite",
  37. "downloaddir": str(os.environ['HOME']) + "/stov",
  38. "maxvideos": "25",
  39. "mailhost": "localhost",
  40. "mailto": "root",
  41. "mailfrom": "stov@localhost",
  42. "smtpport": "25",
  43. "auth_needed": "no",
  44. "user_name": "",
  45. "password": "",
  46. "youtube-dl": "",
  47. "notify": "yes",
  48. "config_version": "10",
  49. "db_version": "6",
  50. "video_codec": "mp4",
  51. "video_height": "1080",
  52. "audio_quality": "bestaudio",
  53. "maxfails": 50,
  54. "check_title": "no"
  55. }
  56. self._explanations = {
  57. "database": _("the name of your database file"),
  58. "downloaddir": _("the directory where downloaded videos are "
  59. "saved"),
  60. "maxvideos": _("the maximum number of videos to retrieve for each "
  61. "subscription"),
  62. "mailhost": _("the host name of your mail server"),
  63. "mailto": _("the address used for notifications"),
  64. "mailfrom": _("the sender address of notification e-mails"),
  65. "smtpport": _("the port to use on your mail server"),
  66. "auth_needed": _("if your mail server requires authentication"),
  67. "user_name": _("the user name used to authenticate to your mail "
  68. "server"),
  69. "password": _("the password used to authenticate to your mail "
  70. "server"),
  71. "youtube-dl": _("the path to your youtube-dl installation"),
  72. "notify": _("if you want to be notified via e-mail about new "
  73. "videos"),
  74. "video_codec": _("which video codec you prefer (h264, webm or "
  75. "flv)"),
  76. "video_height": _("sets the height of the video to download (e.g."
  77. " 1080 for 1920x180)"),
  78. "audio_quality": _("sets the requested audio quality "
  79. "(e.g. bestaudio)"),
  80. "check_title": _("if you want to compare the title of a video "
  81. "with the subscription search string")
  82. }
  83. self.dbpath = str(os.environ['HOME']) + "/.stov/" + \
  84. self.values["database"]
  85. self.outputlevel = "default"
  86. def __new__(cls, *args, **kwargs):
  87. if not Conf._instance:
  88. Conf._instance = super(Conf, cls).__new__(cls)
  89. return Conf._instance
  90. @staticmethod
  91. def get_instance():
  92. """
  93. Returns the singleton instance of the object
  94. :return: Conf object
  95. :rtype: lib_stov.configuration.Conf
  96. """
  97. return Conf._instance
  98. def write_config(self):
  99. """Writes the configuration from the dictionary into the configuration
  100. file for stov. The configuration is written into the home directory of
  101. the user by default.
  102. """
  103. LOGGER.debug(_("Writing configuration file."))
  104. try:
  105. configfile = open(str(os.environ['HOME']) +
  106. "/.stov/stov.json", "w")
  107. except IOError as exc:
  108. raise stov_exceptions.ConfigFileWriteErrorException() from exc
  109. else:
  110. json.dump(self.values, configfile, indent=0)
  111. configfile.close()
  112. def initialize(self):
  113. """Creates the necessary directory for the stov configuration and
  114. calls the internal methods to create the database and the
  115. configuration file.
  116. """
  117. LOGGER.debug(_("Creating stov configuration directory"))
  118. try:
  119. os.mkdir(str(os.environ['HOME']) + "/.stov", 0o750)
  120. except os.error as exc:
  121. raise stov_exceptions.DirectoryCreationFailedException() from exc
  122. else:
  123. self.write_config()
  124. def read_old_config(self):
  125. """Reads the existing plain text configuration file and places the
  126. values in the dictionary. Existing values (such as default values)
  127. are overwritten.
  128. """
  129. LOGGER.debug(_("Opening old configuration file to convert it to "
  130. "json"))
  131. try:
  132. configfile = open(str(os.environ['HOME']) +
  133. "/.stov/stov.config", "r")
  134. except IOError as exc:
  135. raise stov_exceptions.ConfigFileReadErrorException() from exc
  136. for lines in configfile:
  137. tmpline = lines.strip()
  138. tmplist = tmpline.split("=")
  139. self.values[tmplist[0].lower()] = tmplist[1]
  140. configfile.close()
  141. self.dbpath = str(os.environ['HOME']) + "/.stov/" \
  142. + self.values["database"]
  143. def read_config(self):
  144. """Reads the existing json configuration files and loads the values in
  145. the dictionary.
  146. """
  147. LOGGER.debug(_("Opening configuration file"))
  148. try:
  149. configfile = open(str(os.environ['HOME']) + "/.stov/stov.json",
  150. "r")
  151. except IOError as exc:
  152. raise stov_exceptions.ConfigFileReadErrorException() from exc
  153. else:
  154. self.values.update(json.load(configfile))
  155. configfile.close()
  156. def check_config(self):
  157. """Checks if the configuration is up-to-date with the running
  158. stov version.
  159. """
  160. try:
  161. current_version = int(self.values["config_version"])
  162. except ValueError as exc:
  163. raise stov_exceptions.InvalidConfigurationVersionException() \
  164. from exc
  165. self.values["config_version"] = "0"
  166. self.read_config()
  167. if self.values["config_version"] == "0" \
  168. or int(self.values["config_version"]) < current_version:
  169. self.values["config_version"] = str(current_version)
  170. return False
  171. self.values["config_version"] = current_version
  172. return True
  173. def update_config(self):
  174. """Update the configuration to the latest version"""
  175. version_buffer = self.values["config_version"]
  176. self.read_config()
  177. self.values["config_version"] = version_buffer
  178. self.write_config()
  179. def check_db(self):
  180. """Checks the database if it is up-to-date"""
  181. current_db_version = int(self.values["db_version"])
  182. self.values["db_version"] = "0"
  183. self.read_config()
  184. if self.values["db_version"] == "0" or \
  185. int(self.values["db_version"]) < \
  186. int(current_db_version):
  187. self.values["db_version"] = str(current_db_version)
  188. return False
  189. self.values["db_version"] = str(current_db_version)
  190. return True
  191. def assist(self):
  192. """ Ask the user to set all required configuration parameters """
  193. print(_("This assistant will help you to perform the initial "
  194. "configuration of stov. \nThe default value will be "
  195. "displayed in brackets.\n"
  196. "Please specify now :\n"))
  197. for value in self._explanations:
  198. print(self._explanations[value] + " [" + self.values[value] +
  199. "]:" +
  200. " ")
  201. user_input = input()
  202. if user_input != "":
  203. self.values[value] = user_input
  204. self.dbpath = str(os.environ['HOME']) + "/.stov/" + \
  205. self.values["database"]
  206. def migrate_config(self):
  207. """Migrates the configuration from the old plain text config to
  208. the new and shiny json configuration file.
  209. """
  210. try:
  211. self.read_old_config()
  212. self.write_config()
  213. except stov_exceptions.ConfigFileReadErrorException as exc:
  214. raise stov_exceptions.ConfigurationMigrationFailed() from exc
  215. except stov_exceptions.ConfigFileWriteErrorException as exc:
  216. raise stov_exceptions.ConfigurationMigrationFailed from exc
  217. else:
  218. os.remove(str(os.environ['HOME']) + "/.stov/stov.config")
  219. def configure_logging(self, verbose=False, quiet=False):
  220. """
  221. Sets up logging for stov and returns a logger object
  222. :param verbose: whether to use verbose mode
  223. :type verbose: bool
  224. :param quiet: whether to use quiet mode
  225. :type quiet: bool
  226. :return: logger object
  227. :rtype: logging.Logger
  228. """
  229. logger = logging.getLogger("stov")
  230. # verbose takes precedence
  231. if verbose:
  232. logger.setLevel(logging.DEBUG)
  233. self.outputlevel = "verbose"
  234. elif quiet:
  235. logger.setLevel(logging.ERROR)
  236. self.outputlevel = "quiet"
  237. else:
  238. logger.setLevel(logging.INFO)
  239. console_handler = logging.StreamHandler()
  240. logger.addHandler(console_handler)
  241. return logger
  242. def get_value(self, identifier):
  243. """
  244. Returns the value for the specified identifier
  245. :param identifier: the identifier in the value dictionary
  246. :return:
  247. """
  248. return self.values[identifier]