# This file is part of stov, written by Helmut Pozimski 2012-2021. # # stov is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 2 of the License. # # stov is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with stov. If not, see . # -*- coding: utf8 -*- """ This module contains several functions that are necessary to set up the application. """ import argparse import gettext import logging import os import signal import sys from distutils.spawn import find_executable from lib_stov import configuration from lib_stov import database from lib_stov import stov_exceptions LOGGER = logging.getLogger("stov") def initialize_gettext(): """ Installs gettext so localization can be used if necessary. """ # Determine the path where the stov files are for localization locale_path = os.path.join(sys.path[0] + "/locale") if gettext.find("stov", locale_path) is None: base_path = os.path.split(sys.path[0])[0] if "share" in base_path: locale_path = os.path.join(base_path, "locale") else: locale_path = os.path.join(base_path, "share/locale") # Initialize gettext to support translation of the program try: trans = gettext.translation("stov", locale_path) except IOError: gettext.install("stov") if os.environ["LANG"] != "C" and os.environ["LANGUAGE"] != "C": print(_("The translation files could not be found, localization " "won't be available"), file=sys.stderr) else: trans.install() def sighandler(signum, frame): """Handler function for signals caught by stov.""" if signum == 2: print(_("STRG+C has been pressed, quitting..."), file=sys.stderr) elif signum == 15: print(_("Received SIGTERM, quitting..."), file=sys.stderr) os.killpg(os.getpid(), 1) remove_lock() sys.exit(0) def setup_sighandler(): """ Installs a signal handler to catch external signals """ signal.signal(signal.SIGTERM, sighandler) signal.signal(signal.SIGINT, sighandler) def parse_arguments(): """ Uses the argument parser to parse command line arguments and return them :return: parsed arguments :rtype: argparser.ArgumentParser """ parser = argparse.ArgumentParser(conflict_handler="resolve") parser.add_argument("-h", "--help", action="store_true", dest="help", help=_("show this help message and exit")) parser.add_argument("-a", "--add", dest="add", action="store_true", help=_("Add a new subscription (requires either \ --search, --channel or --playlist)")) parser.add_argument("-p", "--playlist", dest="playlist", help=_("Add a new Playlist subscription (requires " "add)")) parser.add_argument("-l", "--lssubs", action="store_true", dest="list", help=_("List the currently available subscriptions")) parser.add_argument("-r", "--remove", type=int, dest="deleteid", help=_("remove a subscription")) parser.add_argument("-u", "--update", nargs="*", dest="update", type=int, default=None, help=_( "update the information about the available " "videos")) parser.add_argument("-d", "--download", nargs="*", type=int, default=None, dest="download", help=_("download all available " "videos which haven't already" " been downloaded")) parser.add_argument("-s", "--search", dest="searchparameter", help=_("optionally add a search string to a new " "channel subscription or create a new search" " subscription (requires --add)")) parser.add_argument("-l", "--lsvids", type=int, dest="subscriptionid", help=_("Print all videos from a subscription")) parser.add_argument("-c", "--catchup", dest="catchup", help=_("Mark all videos from one channel as read \ (requires subscription-id as argument)")) parser.add_argument("-c", "--channel", dest="channel", help=_("specify a channel for a new subscription " "(requires --add)")) parser.add_argument("-l", "--license", dest="license", action="store_true", help=_("show the license of the program")) parser.add_argument("-v", "--version", dest="version", action="store_true", help=_("show the current running version number")) parser.add_argument("-q", "--quiet", dest="quiet", action="store_true", help=_("Suppress all output")) parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", help=_("Be verbose and print also diagnostic " "messages")) parser.add_argument("-c", "--clean-database", dest="cleanup", action="store_true", help=_("Clean the database of entries " "no longer listed in the current API response")) parser.add_argument("-e", "--enable", type=int, dest="enableid", help=_("enables the subscription with the " "provided ID")) parser.add_argument("--disable", type=int, dest="disableid", help=_("disables the subscription with the " "provided ID")) parser.add_argument("-s", "--site", dest="site", help=_("specify the site to work with (for --add)")) parser.add_argument("--lssites", dest="lssites", action="store_true", help=_("list the supported sites")) return parser def create_lock(): """ Creates the lock file for stov. """ if os.access("/tmp/stov.lock", os.F_OK): try: lock_file = open("/tmp/stov.lock", "r") except IOError: LOGGER.error(_("The lock file could not be opened, please " "check that it exists and is readable, " "quitting now")) sys.exit(1) old_pid = lock_file.read().strip() if os.access("/proc/" + old_pid, os.F_OK): LOGGER.error(_("The lock file already exists, probably another " "instance of this program is already running\n" "if you are sure this is not the case, delete it " "manually and try again!")) sys.exit(1) lock_file.close() if not os.access("/proc/" + old_pid, os.F_OK): try: os.remove("/tmp/stov.lock") except os.error: LOGGER.error(_("The old lock file could not be deleted!")) try: lock_file = open("/tmp/stov.lock", "w") lock_file.write(str(os.getpid())) lock_file.close() except IOError: LOGGER.error(_("The lock file could not be created, please check " "that /tmp is writable and properly configured, " "quitting now."), file=sys.stderr) sys.exit(1) def remove_lock(): """ Removes the lock file when exiting the application. """ LOGGER.debug(_("Removing lock file")) try: os.remove("/tmp/stov.lock") sys.exit(0) except os.error: LOGGER.error(_("Could not delete the lock file. Please check what " "went wrong and clean up manually!")) sys.exit(1) def setup_configuration(args): """ Reads the configuration and sets it up to be used within the application. :param args: command line arguments :return: configuration object :rtype: lib_stov.configuration.Conf """ if not os.access(os.environ['HOME'] + "/.stov", os.F_OK & os.W_OK): print(_("This seems to be the first time you run the programm, do " "you want to run the interactive assistant? (yes/no)")) conf = configuration.Conf() logger = conf.configure_logging(args.verbose, args.quiet) temp_input = input() if temp_input == "yes": conf.assist() try: conf.initialize() except stov_exceptions.ConfigFileWriteErrorException as error: logger.error(error) else: logger.info(_("Writing initial configuration according to " "your input, have fun!")) else: logger.info(_("Writing initial configuration according to default" " values.")) logger.debug(_("Creating hidden directory in home for " "configuration and database.")) try: conf.initialize() except stov_exceptions.DirectoryCreationFailedException as error: logger.error(error) except stov_exceptions.ConfigFileWriteErrorException as error: logger.error(error) else: conf = configuration.Conf() logger = conf.configure_logging(args.verbose, args.quiet) if os.access(str(os.environ['HOME']) + "/.stov/stov.config", os.F_OK): logger.debug(_("Migrating configuration from plain text to json.")) conf.migrate_config() try: logger.debug( _("Comparing current and running configuration version.")) check_result = conf.check_config() except stov_exceptions.ConfigFileReadErrorException as error: logger.error(error) sys.exit(1) except stov_exceptions.InvalidConfigurationVersionException as error: logger.error(error) sys.exit(1) else: if not check_result: logger.info( _("Your configuration needs to be updated, performing" " update now.")) try: conf.update_config() except stov_exceptions.ConfigFileReadErrorException as error: logger.error(error) sys.exit(1) except stov_exceptions.ConfigFileWriteErrorException as error: logger.error(error) sys.exit(1) return conf def setup_database(): """ Sets up the database and provides a DB object to talk to the database in the application. :return: database object :rtype: lib_stov.database.Db """ conf = configuration.Conf.get_instance() LOGGER.debug(_("Connecting to stov database")) if os.access(conf.dbpath, os.F_OK): try: db = database.Db(path=conf.dbpath, version=conf.values["db_version"]) except stov_exceptions.DBConnectionFailedException as error: LOGGER.error(error) sys.exit(1) else: try: db = database.Db(path=conf.dbpath, version=conf.values["db_version"]) except stov_exceptions.DBConnectionFailedException as error: LOGGER.error(error) sys.exit(1) else: try: db.populate() except stov_exceptions.DBWriteAccessFailedException as error: LOGGER.error(error) sys.exit(1) else: LOGGER.debug(_("Created initial database tables.")) try: conf = configuration.Conf() LOGGER.debug(_("Comparing current and running database version.")) if not conf.check_db(): LOGGER.info( _("Your database needs to be updated, performing update " "now.")) db.update() conf.values["db_version"] = db.get_version() LOGGER.debug("Opening configuration file.") try: conf.write_config() except stov_exceptions.ConfigFileWriteErrorException as error: LOGGER.error(error) except stov_exceptions.DBWriteAccessFailedException as error: LOGGER.error(error) sys.exit(1) return db def find_youtubedl(): """ Tries to find youtube-dl and writes it's path to the configuration file """ conf = configuration.Conf.get_instance() if conf.values["youtube-dl"] == "": youtubedl_path = find_executable("youtube-dl") conf.values["youtube-dl"] = youtubedl_path if os.access(conf.values["youtube-dl"], os.F_OK & os.R_OK & os.X_OK): LOGGER.info(_("Found youtube-dl at %s, writing it's path to the " "configuration file."), conf.values["youtube-dl"]) LOGGER.debug("Opening configuration file.") try: conf.write_config() except stov_exceptions.ConfigFileWriteErrorException as error: LOGGER.error(error) sys.exit(1) else: LOGGER.error( _("Could not find youtube-dl, it either does not exist, " "is not readable or not executable. Please note that " "youtube-dl is not needed for the program to run but " "is needed to use the download option which won't work " "otherwise. If youtube-dl isn't found automatically, " "you may also enter the path to it in the configuration" " file."))