Update readme

Begin logging
Account for fb2 books without covers
This commit is contained in:
BasioMeusPuga
2019-01-19 01:19:58 +05:30
parent 5e3987dc04
commit 506c458544
25 changed files with 138 additions and 27 deletions

View File

@@ -10,19 +10,30 @@ Currently supports:
* azw / azw3 / azw4
* cbr / cbz
Support for a bunch of other formats is coming. Please see the TODO for additional information.
## Donate to support development
Bitcoin: 17jaxj26vFJNqQ2hEVerbBV5fpTusfqFro
## Requirements
### Needed
| Package | Version tested |
| --- | --- |
| Qt5 | 5.10.1 |
| Python | 3.6 |
| PyQt5 | 5.10.1 |
| python-lxml | 4.3.0 |
| python-beautifulsoup4 | 4.6.0 |
### Optional
| Package | Version tested |
| --- | --- |
| poppler-qt5 | 0.61.1 |
| python-poppler-qt5 | 0.24.2 |
poppler-qt5 and python-poppler-qt5 are optional.
## Support
When reporting issues:
* Include the log `~/.local/share/Lector/Lector.log` AND terminal output.
* If you're having trouble with a book while the rest of the application / other books work, please link to a copy of the book itself.
* If nothing is working, please make sure the requirements mentioned above are all installed, and are at least at the version mentioned.
## Installation
### Manual
@@ -85,12 +96,6 @@ Please keep the translations short. There's only so much space for UI elements.
### Settings window
![alt tag](https://i.imgur.com/l6zJXaH.png)
## Reporting issues
When reporting issues:
* If you're having trouble with a book while the rest of the application / other books work, please link to a copy of the book itself.
* If nothing is working, please make sure the requirements mentioned above are all installed, and are at least at the version mentioned.
## Attributions
* [KindleUnpack](https://github.com/kevinhendricks/KindleUnpack)
* [rarfile](https://github.com/markokr/rarfile)

View File

@@ -19,9 +19,12 @@
import os
import gc
import sys
import logging
import hashlib
import pathlib
from PyQt5 import QtWidgets, QtGui, QtCore
# This allows for the program to be launched from the
# dir where it's been copied instead of needing to be
# installed
@@ -29,7 +32,20 @@ install_dir = os.path.realpath(__file__)
install_dir = pathlib.Path(install_dir).parents[1]
sys.path.append(str(install_dir))
from PyQt5 import QtWidgets, QtGui, QtCore
# Initialize logging
# This is outside the UI declaration to allow sharing
# the object across modules without explicit redeclaration
logger_filename = os.path.join(
QtCore.QStandardPaths.writableLocation(QtCore.QStandardPaths.AppDataLocation),
'Lector',
'Lector.log')
logging.basicConfig(
filename=logger_filename,
filemode='a',
format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
datefmt='%H:%M:%S',
level=logging.ERROR)
logger = logging.getLogger('lector.main')
from lector import database
from lector import sorter
@@ -240,7 +256,7 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# Get list of available parsers
self.available_parsers = '*.' + ' *.'.join(sorter.available_parsers)
print('Available parsers: ' + self.available_parsers)
logger.info('Available parsers: ' + self.available_parsers)
# The Library tab gets no button
self.tabWidget.tabBar().setTabButton(
@@ -407,7 +423,8 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
if not file_paths:
return
print('Attempting to open: ' + ', '.join(file_paths))
logger.info(
'Attempting to open: ' + ', '.join(file_paths))
contents = sorter.BookSorter(
file_paths,
@@ -420,13 +437,18 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
# Notification feedback in case all books return nothing
if not contents:
logger.error('No parseable files found')
return
successfully_opened = []
for i in contents:
# New tabs are created here
# Initial position adjustment is carried out by the tab itself
file_data = contents[i]
Tab(file_data, self)
successfully_opened.append(file_data['path'])
logger.info(
'Successfully opened: ' + ', '.join(file_paths))
if self.settings['last_open_tab'] == 'library':
self.tabWidget.setCurrentIndex(0)
@@ -1041,7 +1063,8 @@ def main():
translations_out_string = '(Translations found)'
if not translations_found:
translations_out_string = '(No translations found)'
print(f'Locale: {QtCore.QLocale.system().name()}', translations_out_string)
log_string = f'Locale: {QtCore.QLocale.system().name()}' + translations_out_string
logger.info(log_string)
form = MainUI()
form.show()
@@ -1050,4 +1073,5 @@ def main():
if __name__ == '__main__':
logger.info('Application start')
main()

View File

@@ -14,10 +14,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from PyQt5 import QtWidgets, QtCore, QtGui
from lector.resources import annotationswindow
logger = logging.getLogger(__name__)
class AnnotationsUI(QtWidgets.QDialog, annotationswindow.Ui_Dialog):
def __init__(self, parent=None):

View File

@@ -16,6 +16,7 @@
import os
import zipfile
import logging
import webbrowser
try:
@@ -29,6 +30,8 @@ from lector.rarfile import rarfile
from lector.threaded import BackGroundCacheRefill
from lector.annotations import AnnotationPlacement
logger = logging.getLogger(__name__)
class PliantQGraphicsView(QtWidgets.QGraphicsView):
def __init__(self, filepath, main_window, parent=None):
@@ -124,7 +127,7 @@ class PliantQGraphicsView(QtWidgets.QGraphicsView):
return bigPixmap
def generate_image_cache(current_page):
print('Building image cache')
logger.info('(Re)building image cache')
current_page_index = all_pages.index(current_page)
# Image caching for single and double page views
@@ -491,7 +494,7 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
selected_index = self.parent.annotationListView.currentIndex()
self.current_annotation = self.parent.annotationModel.data(
selected_index, QtCore.Qt.UserRole)
print('Current annotation: ' + self.current_annotation['name'])
logger.info('Selected annotation: ' + + self.current_annotation['name'])
def mouseReleaseEvent(self, event):
# This takes care of annotation placement

View File

@@ -17,9 +17,12 @@
import os
import pickle
import sqlite3
import logging
from PyQt5 import QtCore
logger = logging.getLogger(__name__)
class DatabaseInit:
def __init__(self, location_prefix):
@@ -81,7 +84,8 @@ class DatabaseInit:
for i in self.books_table_columns.items():
if i[0] not in database_columns:
commit_required = True
print(f'Database: Adding column "{i[0]}"')
info_string = f'Database: Adding column "{i[0]}"'
logger.info(info_string)
sql_command = f"ALTER TABLE books ADD COLUMN {i[0]} {i[1]}"
self.database.execute(sql_command)
@@ -208,7 +212,7 @@ class DatabaseFunctions:
return None
except (KeyError, sqlite3.OperationalError):
print('SQLite is in wretched rebellion @ data fetching handling')
logger.critical('SQLite is in wretched rebellion @ data fetching handling')
def fetch_covers_only(self, hash_list):
parameter_marks = ','.join(['?' for i in hash_list])
@@ -241,7 +245,7 @@ class DatabaseFunctions:
self.database.execute(
sql_command, update_data)
except sqlite3.OperationalError:
print('SQLite is in wretched rebellion @ metadata handling')
logger.critical('SQLite is in wretched rebellion @ metadata handling')
self.database.commit()
self.database.close()

View File

@@ -15,14 +15,20 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import logging
import urllib.request
from PyQt5 import QtWidgets, QtCore, QtGui
logger = logging.getLogger(__name__)
try:
from PyQt5 import QtMultimedia
multimedia_available = True
except ImportError:
print('QtMultimedia not found. Sounds will not play.')
error_string = 'QtMultimedia not found. Sounds will not play.'
print(error_string)
logger.error(error_string)
multimedia_available = False
from lector.resources import definitions

View File

@@ -14,9 +14,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from PyQt5 import QtWidgets, QtGui, QtCore
from lector.resources import pie_chart
logger = logging.getLogger(__name__)
class LibraryDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, temp_dir, parent=None):

View File

@@ -14,12 +14,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from PyQt5 import QtCore, QtGui, QtWidgets
from lector import database
from lector.settings import Settings
from lector.resources import resources
logger = logging.getLogger(__name__)
class QImageFactory:
def __init__(self, parent):

View File

@@ -16,6 +16,7 @@
import os
import pickle
import logging
import pathlib
from PyQt5 import QtGui, QtCore
@@ -23,6 +24,8 @@ from PyQt5 import QtGui, QtCore
from lector import database
from lector.models import TableProxyModel, ItemProxyModel
logger = logging.getLogger(__name__)
class Library:
def __init__(self, parent):
@@ -47,7 +50,7 @@ class Library:
'LIKE')
if not books:
print('Database returned nothing')
logger.error('Database returned nothing')
return
elif mode == 'addition':
@@ -320,7 +323,7 @@ class Library:
addition_mode = item_metadata['addition_mode']
except KeyError:
addition_mode = 'automatic'
print('Libary: Error setting addition mode for prune')
logger.error('Libary: Error setting addition mode for prune')
if (book_path not in valid_paths and
(addition_mode != 'manual' or addition_mode is None)):

View File

@@ -14,12 +14,16 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from PyQt5 import QtWidgets, QtCore, QtGui
from lector import database
from lector.widgets import PliantQGraphicsScene
from lector.resources import metadata
logger = logging.getLogger(__name__)
class MetadataUI(QtWidgets.QDialog, metadata.Ui_Dialog):
def __init__(self, parent):

View File

@@ -14,11 +14,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import pathlib
from PyQt5 import QtCore, QtWidgets
from lector.resources import pie_chart
logger = logging.getLogger(__name__)
class BookmarkProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):

View File

@@ -19,10 +19,13 @@
import os
import time
import logging
import zipfile
from lector.rarfile import rarfile
logger = logging.getLogger(__name__)
class ParseCOMIC:
def __init__(self, filename, *args):

View File

@@ -16,9 +16,12 @@
import os
import zipfile
import logging
from lector.readers.read_epub import EPUB
logger = logging.getLogger(__name__)
class ParseEPUB:
def __init__(self, filename, temp_dir, file_md5):

View File

@@ -15,9 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import logging
from lector.readers.read_fb2 import FB2
logger = logging.getLogger(__name__)
class ParseFB2:
def __init__(self, filename, temp_dir, file_md5):

View File

@@ -21,10 +21,13 @@ import os
import sys
import shutil
import zipfile
import logging
from lector.readers.read_epub import EPUB
import lector.KindleUnpack.kindleunpack as KindleUnpack
logger = logging.getLogger(__name__)
class ParseMOBI:
def __init__(self, filename, temp_dir, file_md5):

View File

@@ -20,12 +20,15 @@
import io
import os
import logging
from PyQt5 import QtCore
from bs4 import BeautifulSoup
import popplerqt5
logger = logging.getLogger(__name__)
class ParsePDF:
def __init__(self, filename, *args):

View File

@@ -15,11 +15,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import logging
import zipfile
from urllib.parse import unquote
from bs4 import BeautifulSoup
logger = logging.getLogger(__name__)
class EPUB:
def __init__(self, filename):
@@ -189,7 +192,7 @@ class EPUB:
if biggest_image:
self.book['cover'] = self.read_from_zip(biggest_image)
else:
print('No cover found for: ' + self.filename)
logger.error('No cover found for: ' + self.filename)
# Parse spine and arrange chapter paths acquired from the opf
# according to the order IN THE SPINE

View File

@@ -17,9 +17,12 @@
import os
import base64
import zipfile
import logging
from bs4 import BeautifulSoup
logger = logging.getLogger(__name__)
class FB2:
def __init__(self, filename):
@@ -83,7 +86,9 @@ class FB2:
for i in cover_image_data:
if cover_image_name.endswith(i.get('id')):
self.book['cover'] = base64.decodebytes(i.text.encode())
except AttributeError:
except (AttributeError, TypeError):
# Catch TypeError in case no images exist in the book
logger.error('No cover found for: ' + self.filename)
self.book['cover'] = None
def parse_chapters(self, temp_dir):

View File

@@ -10,5 +10,6 @@
<p>Author: BasioMeusPuga <a href="mailto:disgruntled.mob@gmail.com">disgruntled.mob@gmail.com</a></p>
<p>Page:&nbsp;<a href="https://github.com/BasioMeusPuga/Lector">https://github.com/BasioMeusPuga/Lector</a></p>
<p>License: GPLv3&nbsp;<a href="https://www.gnu.org/licenses/gpl-3.0.en.html">https://www.gnu.org/licenses/gpl-3.0.en.html</a></p>
<p>Donate (Bitcoin): 17jaxj26vFJNqQ2hEVerbBV5fpTusfqFro</p>
<p>&nbsp;</p></body>
</html>

View File

@@ -17,10 +17,13 @@
# Keep in mind that all integer / boolean settings are returned as strings
import os
import logging
from ast import literal_eval
from PyQt5 import QtCore, QtGui
logger = logging.getLogger(__name__)
class Settings:
def __init__(self, parent):

View File

@@ -19,6 +19,7 @@
import os
import copy
import logging
import pathlib
from PyQt5 import QtWidgets, QtCore, QtGui
@@ -30,6 +31,8 @@ from lector.threaded import BackGroundBookSearch, BackGroundBookAddition
from lector.resources import settingswindow
from lector.settings import Settings
logger = logging.getLogger(__name__)
class SettingsUI(QtWidgets.QDialog, settingswindow.Ui_Dialog):
def __init__(self, parent=None):
@@ -189,7 +192,7 @@ class SettingsUI(QtWidgets.QDialog, settingswindow.Ui_Dialog):
self.main_window.generate_library_filter_menu(paths)
directory_data = {}
if not paths:
print('Database: No paths for settings...')
logger.error('Database: No paths for settings...')
else:
# Convert to the dictionary format that is
# to be fed into the QFileSystemModel

View File

@@ -36,6 +36,7 @@ import os
import sys
import time
import pickle
import logging
import hashlib
import threading
@@ -53,6 +54,8 @@ from lector.parsers.mobi import ParseMOBI
from lector.parsers.fb2 import ParseFB2
from lector.parsers.comicbooks import ParseCOMIC
logger = logging.getLogger(__name__)
sorter = {
'epub': ParseEPUB,
'mobi': ParseMOBI,
@@ -70,7 +73,9 @@ try:
from lector.parsers.pdf import ParsePDF
sorter['pdf'] = ParsePDF
except ImportError:
print('python-poppler-qt5 is not installed. Pdf files will not work.')
error_string = 'python-poppler-qt5 is not installed. Pdf files will not work.'
print(error_string)
logger.error(error_string)
available_parsers = [i for i in sorter]
progressbar = None # This is populated by __main__
@@ -194,7 +199,7 @@ class BookSorter:
# None values are accounted for here
is_valid = book_ref.read_book()
if not is_valid:
print('Cannot parse: ' + filename)
logger.error('Cannot parse:' + filename)
return
if book_ref.book:
@@ -311,7 +316,8 @@ class BookSorter:
return_books[j] = i[j]
del self.processed_books
print('Finished processing in', time.time() - start_time)
processing_time = str(time.time() - start_time)
logger.info('Finished processing in:' + processing_time)
return return_books

View File

@@ -16,6 +16,7 @@
import os
import re
import logging
import pathlib
from multiprocessing.dummy import Pool
@@ -24,6 +25,8 @@ from PyQt5 import QtCore, QtGui
from lector import sorter
from lector import database
logger = logging.getLogger(__name__)
class BackGroundTabUpdate(QtCore.QThread):
def __init__(self, database_path, all_metadata, parent=None):

View File

@@ -14,8 +14,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
from PyQt5 import QtWidgets, QtCore
logger = logging.getLogger(__name__)
class BookToolBar(QtWidgets.QToolBar):
def __init__(self, parent=None):

View File

@@ -20,6 +20,7 @@
import os
import uuid
import logging
from PyQt5 import QtWidgets, QtGui, QtCore
@@ -28,6 +29,8 @@ from lector.sorter import resize_image
from lector.threaded import BackGroundTextSearch
from lector.contentwidgets import PliantQGraphicsView, PliantQTextBrowser
logger = logging.getLogger(__name__)
class Tab(QtWidgets.QWidget):
def __init__(self, metadata, main_window, parent=None):