diff --git a/lector/__main__.py b/lector/__main__.py index 914d529..8d4f5d8 100755 --- a/lector/__main__.py +++ b/lector/__main__.py @@ -19,7 +19,6 @@ import os import gc import sys -import logging import hashlib import pathlib @@ -30,6 +29,13 @@ install_dir = os.path.realpath(__file__) install_dir = pathlib.Path(install_dir).parents[1] sys.path.append(str(install_dir)) +# Init logging +# Must be done first and at the module level +# or it won't work properly in case of the imports below +from lector.logger import init_logging +logger = init_logging(sys.argv) +logger.log(60, 'Application started') + from PyQt5 import QtWidgets, QtGui, QtCore from lector import database @@ -85,10 +91,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): Settings(self).read_settings() # This should populate all variables that need # to be remembered across sessions - # Initialize logging - self.logger = init_logging(self.settings['log_level']) - self.logger.log(60, 'Application started') - # Initialize icon factory self.QImageFactory = QImageFactory(self) @@ -245,7 +247,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): # Get list of available parsers self.available_parsers = '*.' + ' *.'.join(sorter.available_parsers) - self.logger.info('Available parsers: ' + self.available_parsers) + logger.info('Available parsers: ' + self.available_parsers) # The Library tab gets no button self.tabWidget.tabBar().setTabButton( @@ -412,7 +414,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): if not file_paths: return - self.logger.info( + logger.info( 'Attempting to open: ' + ', '.join(file_paths)) contents = sorter.BookSorter( @@ -426,7 +428,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): # Notification feedback in case all books return nothing if not contents: - self.logger.error('No parseable files found') + logger.error('No parseable files found') return successfully_opened = [] @@ -436,7 +438,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): file_data = contents[i] Tab(file_data, self) successfully_opened.append(file_data['path']) - self.logger.info( + logger.info( 'Successfully opened: ' + ', '.join(file_paths)) if self.settings['last_open_tab'] == 'library': @@ -1038,21 +1040,6 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow): QtWidgets.qApp.exit() -def init_logging(log_level): - location_prefix = QtCore.QStandardPaths.writableLocation( - QtCore.QStandardPaths.AppDataLocation) - os.makedirs(location_prefix, exist_ok=True) - logger_filename = os.path.join(location_prefix, 'Lector.log') - logging.basicConfig( - filename=logger_filename, - filemode='a', - format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', - datefmt='%Y/%M/%d %H:%M:%S', - level=log_level) - logging.addLevelName(60, 'HammerTime') ## Messages that MUST be logged - return logging.getLogger('lector.main') - - def main(): app = QtWidgets.QApplication(sys.argv) app.setApplicationName('Lector') # This is needed for QStandardPaths diff --git a/lector/logger.py b/lector/logger.py new file mode 100644 index 0000000..b8d94d7 --- /dev/null +++ b/lector/logger.py @@ -0,0 +1,54 @@ +# This file is a part of Lector, a Qt based ebook reader +# Copyright (C) 2017-2019 BasioMeusPuga + +# This program 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, either version 3 of the License, or +# (at your option) any later version. + +# This program 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 this program. If not, see . + +import os +import logging + +from PyQt5 import QtCore + + +def init_logging(cli_arguments): + # This needs a separate 'Lector' in the os.path.join because + # application name isn't explicitly set in this module + location_prefix = os.path.join( + QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppDataLocation), + 'Lector') + os.makedirs(location_prefix, exist_ok=True) + logger_filename = os.path.join(location_prefix, 'Lector.log') + + log_level = 30 # Warning and above + # Set log level according to command line arguments + try: + if cli_arguments[1] == 'debug': + log_level = 10 # Debug and above + print('Debug logging enabled') + try: + os.remove(logger_filename) # Remove old log for clarity + except FileNotFoundError: + pass + except IndexError: + pass + + # Create logging object + logging.basicConfig( + filename=logger_filename, + filemode='a', + format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s', + datefmt='%Y/%m/%d %H:%M:%S', + level=log_level) + logging.addLevelName(60, 'HAMMERTIME') ## Messages that MUST be logged + + return logging.getLogger('lector.main') diff --git a/lector/settings.py b/lector/settings.py index 949cb71..d5444a4 100644 --- a/lector/settings.py +++ b/lector/settings.py @@ -88,11 +88,6 @@ class Settings: 'currentProfileIndex', 0)) self.parent.comic_profile = self.settings.value( 'comicProfile', self.default_comic_profile) - try: - log_level = int(self.settings.value('logLevel', logging.WARNING)) - except ValueError: - log_level = 30 - self.parent.settings['log_level'] = log_level self.settings.endGroup() self.settings.beginGroup('lastOpen') @@ -153,6 +148,8 @@ class Settings: self.parent.settings['annotations'] = list() self.settings.endGroup() + logger.info('Settings loaded') + def save_settings(self): current_settings = self.parent.settings @@ -187,7 +184,6 @@ class Settings: current_profile3]) self.settings.setValue('currentProfileIndex', current_profile_index) self.settings.setValue('comicProfile', self.parent.comic_profile) - self.settings.setValue('logLevel', self.parent.settings['log_level']) self.settings.endGroup() current_tab_index = self.parent.tabWidget.currentIndex() diff --git a/lector/settingsdialog.py b/lector/settingsdialog.py index be1f6b1..b04fbd9 100644 --- a/lector/settingsdialog.py +++ b/lector/settingsdialog.py @@ -192,7 +192,7 @@ class SettingsUI(QtWidgets.QDialog, settingswindow.Ui_Dialog): self.main_window.generate_library_filter_menu(paths) directory_data = {} if not paths: - logger.warning('No paths saved for books') + logger.warning('No book paths saved') else: # Convert to the dictionary format that is # to be fed into the QFileSystemModel @@ -264,9 +264,14 @@ class SettingsUI(QtWidgets.QDialog, settingswindow.Ui_Dialog): self.database_path).set_library_paths(data_pairs) if not data_pairs: + logger.error('Can\'t scan - No book paths saved') try: if self.sender().objectName() == 'reloadLibrary': self.show() + treeViewIndex = self.listModel.index(0, 0) + self.listView.setCurrentIndex(treeViewIndex) + self.page_switch(treeViewIndex) + return except AttributeError: pass diff --git a/lector/sorter.py b/lector/sorter.py index 06e5392..674b708 100644 --- a/lector/sorter.py +++ b/lector/sorter.py @@ -39,6 +39,7 @@ import pickle import logging import hashlib import threading +import importlib # The multiprocessing module does not work correctly on Windows if sys.platform.startswith('win'): @@ -57,26 +58,38 @@ from lector.parsers.comicbooks import ParseCOMIC logger = logging.getLogger(__name__) sorter = { - 'epub': ParseEPUB, - 'mobi': ParseMOBI, - 'azw': ParseMOBI, - 'azw3': ParseMOBI, - 'azw4': ParseMOBI, - 'prc': ParseMOBI, - 'fb2': ParseFB2, - 'fb2.zip': ParseFB2, 'cbz': ParseCOMIC, 'cbr': ParseCOMIC} -# The following imports are for optional dependencies -try: +# Check what dependencies are installed +# python-poppler-qt5 - Optional +poppler_check = importlib.util.find_spec('popplerqt5') +if poppler_check: from lector.parsers.pdf import ParsePDF sorter['pdf'] = ParsePDF -except ImportError: - error_string = 'python-poppler-qt5 is not installed. Pdf files will not work.' +else: + error_string = 'python-poppler-qt5 is not installed. Will be unable to load PDFs.' print(error_string) logger.error(error_string) +# python-lxml - Required for everything except comics +lxml_check = importlib.util.find_spec('lxml') +if lxml_check: + lxml_dependent = { + 'epub': ParseEPUB, + 'mobi': ParseMOBI, + 'azw': ParseMOBI, + 'azw3': ParseMOBI, + 'azw4': ParseMOBI, + 'prc': ParseMOBI, + 'fb2': ParseFB2, + 'fb2.zip': ParseFB2} + sorter.update(lxml_dependent) +else: + critical_sting = 'python-lxml is not installed. Only comics will load.' + print(critical_sting) + logger.critical(critical_sting) + available_parsers = [i for i in sorter] progressbar = None # This is populated by __main__ progress_emitter = None # This is to be made into a global variable diff --git a/lector/threaded.py b/lector/threaded.py index 63aee44..96de0ab 100644 --- a/lector/threaded.py +++ b/lector/threaded.py @@ -124,8 +124,11 @@ class BackGroundBookSearch(QtCore.QThread): if self.valid_directories: initiate_threads() - info_string = str(len(self.valid_files)) + ' books found' - logger.info(info_string) + if self.valid_files: + info_string = str(len(self.valid_files)) + ' books found' + logger.info(info_string) + else: + logger.error('No books found on scan') else: logger.error('No valid directories') diff --git a/lector/toolbars.py b/lector/toolbars.py index 775ed47..54e6107 100644 --- a/lector/toolbars.py +++ b/lector/toolbars.py @@ -407,6 +407,7 @@ class LibraryToolBar(QtWidgets.QToolBar): image_factory.get_image('reload'), self._translate('LibraryToolBar', 'Scan Library'), self) + self.reloadLibraryButton.setObjectName('reloadLibrary') self.libraryFilterButton = QtWidgets.QToolButton(self) self.libraryFilterButton.setIcon(image_factory.get_image('view-readermode')) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1e8fedb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +beautifulsoup4==4.7.1 +lxml==4.3.0 +PyQt5==5.11.3 +PyQt5-sip==4.19.13 +soupsieve==1.7.2