From a8040ad3fa9fd7a9c5b563943d92cc6057a8ec87 Mon Sep 17 00:00:00 2001 From: OzzieIsaacs Date: Sat, 14 Jul 2018 08:31:52 +0200 Subject: [PATCH] #538: -Refactoring gdrive and file handling -Improved error handling for gdrive -bugfix "gdrive stopping after a while" - Renaming book title working - Still Bugs in upload file to gdrive and renaming author --- cps/gdriveutils.py | 120 ++++++++++++++++++++++----- cps/helper.py | 85 +++++++++++++------- cps/server.py | 2 - cps/ub.py | 14 ++++ cps/web.py | 196 +++++++++++++++++---------------------------- 5 files changed, 244 insertions(+), 173 deletions(-) diff --git a/cps/gdriveutils.py b/cps/gdriveutils.py index 5073f359..69c6c091 100644 --- a/cps/gdriveutils.py +++ b/cps/gdriveutils.py @@ -5,9 +5,9 @@ try: except ImportError: pass import os - from ub import config import cli +import shutil from sqlalchemy import * from sqlalchemy.ext.declarative import declarative_base @@ -16,6 +16,57 @@ from sqlalchemy.orm import * import web +class Singleton: + """ + A non-thread-safe helper class to ease implementing singletons. + This should be used as a decorator -- not a metaclass -- to the + class that should be a singleton. + + The decorated class can define one `__init__` function that + takes only the `self` argument. Also, the decorated class cannot be + inherited from. Other than that, there are no restrictions that apply + to the decorated class. + + To get the singleton instance, use the `Instance` method. Trying + to use `__call__` will result in a `TypeError` being raised. + + """ + + def __init__(self, decorated): + self._decorated = decorated + + def Instance(self): + """ + Returns the singleton instance. Upon its first call, it creates a + new instance of the decorated class and calls its `__init__` method. + On all subsequent calls, the already created instance is returned. + + """ + try: + return self._instance + except AttributeError: + self._instance = self._decorated() + return self._instance + + def __call__(self): + raise TypeError('Singletons must be accessed through `Instance()`.') + + def __instancecheck__(self, inst): + return isinstance(inst, self._decorated) + + +@Singleton +class Gauth: + def __init__(self): + self.auth = GoogleAuth(settings_file=os.path.join(config.get_main_dir,'settings.yaml')) + + +@Singleton +class Gdrive: + def __init__(self): + self.drive = getDrive(gauth=Gauth.Instance().auth) + + engine = create_engine('sqlite:///{0}'.format(cli.gdpath), echo=False) Base = declarative_base() @@ -110,9 +161,12 @@ def getFolderInFolder(parentId, folderName,drive=None): query = "title = '%s' and " % folderName.replace("'", "\\'") folder = query + "'%s' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % parentId fileList = drive.ListFile({'q': folder}).GetList() - return fileList[0] - + if fileList.__len__() == 0: + return None + else: + return fileList[0] +# Search for id of root folder in gdrive database, if not found request from gdrive and store in internal database def getEbooksFolderId(drive=None): storedPathName = session.query(GdriveId).filter(GdriveId.path == '/').first() if storedPathName: @@ -131,11 +185,14 @@ def getEbooksFolderId(drive=None): def getFile(pathId, fileName, drive=None): - drive = getDrive(drive) + drive = getDrive(Gdrive.Instance().drive) metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'")) fileList = drive.ListFile({'q': metaDataFile}).GetList() - return fileList[0] + if fileList.__len__() == 0: + return None + else: + return fileList[0] def getFolderId(path, drive=None): @@ -156,12 +213,17 @@ def getFolderId(path, drive=None): if storedPathName: currentFolderId = storedPathName.gdrive_id else: - currentFolderId = getFolderInFolder(currentFolderId, x, drive)['id'] - gDriveId = GdriveId() - gDriveId.gdrive_id = currentFolderId - gDriveId.path = currentPath - session.merge(gDriveId) - dbChange = True + currentFolder = getFolderInFolder(currentFolderId, x, drive) + if currentFolder: + gDriveId = GdriveId() + gDriveId.gdrive_id = currentFolder['id'] + gDriveId.path = currentPath + session.merge(gDriveId) + dbChange = True + currentFolderId = currentFolder['id'] + else: + currentFolderId= None + break if dbChange: session.commit() else: @@ -169,15 +231,17 @@ def getFolderId(path, drive=None): return currentFolderId -def getFileFromEbooksFolder(drive, path, fileName): - drive = getDrive(drive) +def getFileFromEbooksFolder(path, fileName): + drive = getDrive(Gdrive.Instance().drive) if path: # sqlCheckPath=path if path[-1] =='/' else path + '/' folderId = getFolderId(path, drive) else: folderId = getEbooksFolderId(drive) - - return getFile(folderId, fileName, drive) + if folderId: + return getFile(folderId, fileName, drive) + else: + return None def copyDriveFileRemote(drive, origin_file_id, copy_title): @@ -192,9 +256,9 @@ def copyDriveFileRemote(drive, origin_file_id, copy_title): return None -def downloadFile(drive, path, filename, output): - drive = getDrive(drive) - f = getFileFromEbooksFolder(drive, path, filename) +def downloadFile(path, filename, output): + # drive = getDrive(drive) + f = getFileFromEbooksFolder(path, filename) f.GetContentFile(output) @@ -238,8 +302,8 @@ def copyToDrive(drive, uploadFile, createRoot, replaceFiles, driveFile.Upload() -def uploadFileToEbooksFolder(drive, destFile, f): - drive = getDrive(drive) +def uploadFileToEbooksFolder(destFile, f): + drive = getDrive(Gdrive.Instance().drive) parent = getEbooksFolder(drive) splitDir = destFile.split('/') for i, x in enumerate(splitDir): @@ -349,10 +413,24 @@ def getChangeById (drive, change_id): change = drive.auth.service.changes().get(changeId=change_id).execute() return change except (errors.HttpError) as error: - web.app.logger.exception(error) + app.logger.info(error.message) return None # Deletes the local hashes database to force search for new folder names def deleteDatabaseOnChange(): session.query(GdriveId).delete() session.commit() + +def updateGdriveCalibreFromLocal(): + backupCalibreDbAndOptionalDownload(Gdrive.Instance().drive) + copyToDrive(Gdrive.Instance().drive, config.config_calibre_dir, False, True) + for x in os.listdir(config.config_calibre_dir): + if os.path.isdir(os.path.join(config.config_calibre_dir, x)): + shutil.rmtree(os.path.join(config.config_calibre_dir, x)) + +# update gdrive.db on edit of books title +def updateDatabaseOnEdit(ID,newPath): + storedPathName = session.query(GdriveId).filter(GdriveId.gdrive_id == ID).first() + if storedPathName: + storedPathName.path = newPath + session.commit() diff --git a/cps/helper.py b/cps/helper.py index 7eb3bd53..f70c92bf 100755 --- a/cps/helper.py +++ b/cps/helper.py @@ -56,16 +56,6 @@ RET_SUCCESS = 1 RET_FAIL = 0 -def update_download(book_id, user_id): - check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id == - book_id).first() - - if not check: - new_download = ub.Downloads(user_id=user_id, book_id=book_id) - ub.session.add(new_download) - ub.session.commit() - - def make_mobi(book_id, calibrepath): error_message = None vendorpath = os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + @@ -243,7 +233,6 @@ def send_mail(book_id, kindle_mail, calibrepath): def get_attachment(file_path): """Get file as MIMEBase message""" - try: file_ = open(file_path, 'rb') attachment = MIMEBase('application', 'octet-stream') @@ -306,7 +295,7 @@ def get_sorted_author(value): return value2 -def delete_book(book, calibrepath): +def delete_book_file(book, calibrepath): # check that path is 2 elements deep, check that target path has no subfolders if "/" in book.path: path = os.path.join(calibrepath, book.path) @@ -314,16 +303,8 @@ def delete_book(book, calibrepath): else: logging.getLogger('cps.web').error("Deleting book " + str(book.id) + " failed, book path value: "+ book.path) -# ToDo: Implement delete book on gdrive -def delete_book_gdrive(book): - # delete book and path of book in gdrive.db - # delete book and path of book on gdrive - #gFile = gd.getFileFromEbooksFolder(web.Gdrive.Instance().drive, os.path.dirname(book.path), titledir) - #gFile.Trash() - pass - -def update_dir_stucture(book_id, calibrepath): +def update_dir_stucture_file(book_id, calibrepath): localbook = db.session.query(db.Books).filter(db.Books.id == book_id).first() path = os.path.join(calibrepath, localbook.path) @@ -372,18 +353,64 @@ def update_dir_structure_gdrive(book_id): if titledir != new_titledir: # print (titledir) - gFile = gd.getFileFromEbooksFolder(web.Gdrive.Instance().drive, os.path.dirname(book.path), titledir) - gFile['title'] = new_titledir - gFile.Upload() - book.path = book.path.split('/')[0] + '/' + new_titledir + gFile = gd.getFileFromEbooksFolder(os.path.dirname(book.path), titledir) + if gFile: + gFile['title'] = new_titledir + + gFile.Upload() + book.path = book.path.split('/')[0] + '/' + new_titledir + gd.updateDatabaseOnEdit(gFile['id'], book.path) # only child folder affected + else: + error = _(u'File %s not found on gdrive' % book.path) # file not found if authordir != new_authordir: - gFile = gd.getFileFromEbooksFolder(web.Gdrive.Instance().drive, None, authordir) - gFile['title'] = new_authordir - gFile.Upload() - book.path = new_authordir + '/' + book.path.split('/')[1] + gFile = gd.getFileFromEbooksFolder(os.path.dirname(book.path), titledir) + # gFileDirOrig = gd.getFileFromEbooksFolder(None, authordir) + if gFile: + # check if authordir exisits + gFileDirOrig = gd.getFileFromEbooksFolder(None, authordir) + if gFileDirOrig: + gFile['parents'].append({"id": gFileDirOrig['id']}) + gFile.Upload() + else: + # Folder is not exisiting + #parent = drive.CreateFile({'title': authordir, 'parents': [{"kind": "drive#fileLink", 'id': root folder id}], + # "mimeType": "application/vnd.google-apps.folder"}) + parent.Upload() + # gFile['title'] = new_authordir + # gFile.Upload() + book.path = new_authordir + '/' + book.path.split('/')[1] + gd.updateDatabaseOnEdit(gFile['id'], book.path) + # Todo last element from parent folder moved to different folder, what to do with parent folder? + # parent folder affected + else: + error = _(u'File %s not found on gdrive' % authordir) # file not found return error +# ToDo: Implement delete book on gdrive +def delete_book_gdrive(book): + # delete book and path of book in gdrive.db + # delete book and path of book on gdrive + #gFile = gd.getFileFromEbooksFolder(os.path.dirname(book.path), titledir) + #gFile.Trash() + pass + + +################################## External interface + +def update_dir_stucture(book_id, calibrepath): + if ub.config.config_use_google_drive: + return update_dir_structure_gdrive(book_id) + else: + return update_dir_stucture_file(book_id, calibrepath) + +def delete_book(book, calibrepath): + if ub.config.config_use_google_drive: + return delete_book_file(book, calibrepath) + else: + return delete_book_gdrive(book) +################################## + class Updater(threading.Thread): diff --git a/cps/server.py b/cps/server.py index b84c71dc..0842c064 100644 --- a/cps/server.py +++ b/cps/server.py @@ -7,7 +7,6 @@ import sys import os try: from gevent.pywsgi import WSGIServer - from gevent import monkey from gevent.pool import Pool from gevent import __version__ as geventVersion gevent_present = True @@ -52,7 +51,6 @@ class server: if gevent_present: web.app.logger.info('Starting Gevent server') # leave subprocess out to allow forking for fetchers and processors - monkey.patch_all(subprocess=False) self.start_gevent() else: web.app.logger.info('Starting Tornado server') diff --git a/cps/ub.py b/cps/ub.py index 1824fc42..fdcd8ee7 100644 --- a/cps/ub.py +++ b/cps/ub.py @@ -691,6 +691,20 @@ def get_mail_settings(): return data +# Save downloaded books per user in calibre-web's own database +def update_download(book_id, user_id): + check = session.query(Downloads).filter(Downloads.user_id == user_id).filter(Downloads.book_id == + book_id).first() + + if not check: + new_download = Downloads(user_id=user_id, book_id=book_id) + session.add(new_download) + session.commit() + +# Delete non exisiting downloaded books in calibre-web's own database +def delete_download(book_id): + session.query(Downloads).filter(book_id == Downloads.book_id).delete() + session.commit() # Generate user Guest (translated text), as anoymous user, no rights def create_anonymous_user(): diff --git a/cps/web.py b/cps/web.py index 49977dad..c788ac9c 100755 --- a/cps/web.py +++ b/cps/web.py @@ -50,7 +50,7 @@ from flask_principal import __version__ as flask_principalVersion from flask_babel import Babel from flask_babel import gettext as _ import requests -import zipfile +# import zipfile from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.datastructures import Headers from babel import Locale as LC @@ -77,7 +77,6 @@ import tempfile import hashlib from redirect import redirect_back, is_safe_url - try: from urllib.parse import quote from imp import reload @@ -109,57 +108,6 @@ def md5(fname): return hash_md5.hexdigest() -class Singleton: - """ - A non-thread-safe helper class to ease implementing singletons. - This should be used as a decorator -- not a metaclass -- to the - class that should be a singleton. - - The decorated class can define one `__init__` function that - takes only the `self` argument. Also, the decorated class cannot be - inherited from. Other than that, there are no restrictions that apply - to the decorated class. - - To get the singleton instance, use the `Instance` method. Trying - to use `__call__` will result in a `TypeError` being raised. - - """ - - def __init__(self, decorated): - self._decorated = decorated - - def Instance(self): - """ - Returns the singleton instance. Upon its first call, it creates a - new instance of the decorated class and calls its `__init__` method. - On all subsequent calls, the already created instance is returned. - - """ - try: - return self._instance - except AttributeError: - self._instance = self._decorated() - return self._instance - - def __call__(self): - raise TypeError('Singletons must be accessed through `Instance()`.') - - def __instancecheck__(self, inst): - return isinstance(inst, self._decorated) - - -@Singleton -class Gauth: - def __init__(self): - self.auth = GoogleAuth(settings_file=os.path.join(config.get_main_dir,'settings.yaml')) - - -@Singleton -class Gdrive: - def __init__(self): - self.drive = gdriveutils.getDrive(gauth=Gauth.Instance().auth) - - class ReverseProxied(object): """Wrap the application in this middleware and configure the front-end server to add these headers, to let you quietly bind @@ -305,14 +253,6 @@ def authenticate(): {'WWW-Authenticate': 'Basic realm="Login Required"'}) -def updateGdriveCalibreFromLocal(): - gdriveutils.backupCalibreDbAndOptionalDownload(Gdrive.Instance().drive) - gdriveutils.copyToDrive(Gdrive.Instance().drive, config.config_calibre_dir, False, True) - for x in os.listdir(config.config_calibre_dir): - if os.path.isdir(os.path.join(config.config_calibre_dir, x)): - shutil.rmtree(os.path.join(config.config_calibre_dir, x)) - - def requires_basic_auth_if_no_ano(f): @wraps(f) def decorated(*args, **kwargs): @@ -736,8 +676,9 @@ def feed_hot(): .filter(db.Books.id == book.Downloads.book_id).first() ) else: - ub.session.query(ub.Downloads).filter(book.Downloads.book_id == ub.Downloads.book_id).delete() - ub.session.commit() + ub.delete_download(book.Downloads.book_id) + # ub.session.query(ub.Downloads).filter(book.Downloads.book_id == ub.Downloads.book_id).delete() + # ub.session.commit() numBooks = entries.__len__() pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, numBooks) xml = render_title_template('feed.xml', entries=entries, pagination=pagination) @@ -920,7 +861,7 @@ def get_opds_download_link(book_id, book_format): data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()).first() app.logger.info(data.name) if current_user.is_authenticated: - helper.update_download(book_id, int(current_user.id)) + ub.update_download(book_id, int(current_user.id)) file_name = book.title if len(book.authors) > 0: file_name = book.authors[0].name + '_' + file_name @@ -935,7 +876,7 @@ def get_opds_download_link(book_id, book_format): startTime = time.time() if config.config_use_google_drive: app.logger.info(time.time() - startTime) - df = gdriveutils.getFileFromEbooksFolder(Gdrive.Instance().drive, book.path, data.name + "." + book_format) + df = gdriveutils.getFileFromEbooksFolder(book.path, data.name + "." + book_format) return do_gdrive_download(df, headers) else: response = make_response(send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + book_format)) @@ -1158,8 +1099,9 @@ def hot_books(page): if downloadBook: entries.append(downloadBook) else: - ub.session.query(ub.Downloads).filter(book.Downloads.book_id == ub.Downloads.book_id).delete() - ub.session.commit() + ub.delete_download(book.Downloads.book_id) + # ub.session.query(ub.Downloads).filter(book.Downloads.book_id == ub.Downloads.book_id).delete() + # ub.session.commit() numBooks = entries.__len__() pagination = Pagination(page, config.config_books_per_page, numBooks) return render_title_template('index.html', random=random, entries=entries, pagination=pagination, @@ -1486,13 +1428,12 @@ def delete_book(book_id): # delete book from Shelfs, Downloads, Read list ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).delete() ub.session.query(ub.ReadBook).filter(ub.ReadBook.book_id == book_id).delete() - ub.session.query(ub.Downloads).filter(ub.Downloads.book_id == book_id).delete() + # ToDo check Downloads.book right + ub.delete_download(book_id) + # ub.session.query(ub.Downloads).filter(ub.Downloads.book_id == book_id).delete() ub.session.commit() - if config.config_use_google_drive: - helper.delete_book_gdrive(book) # ToDo really delete file - else: - helper.delete_book(book, config.config_calibre_dir) + helper.delete_book(book, config.config_calibre_dir) # check if only this book links to: # author, language, series, tags, custom columns modify_database_object([u''], book.authors, db.Authors, db.session, 'author') @@ -1560,7 +1501,7 @@ def watch_gdrive(): address = '%s/gdrive/watch/callback' % filedata['web']['redirect_uris'][0] notification_id = str(uuid4()) try: - result = gdriveutils.watchChange(Gdrive.Instance().drive, notification_id, + result = gdriveutils.watchChange(gdriveutils.Gdrive.Instance().drive, notification_id, 'web_hook', address, gdrive_watch_callback_token, current_milli_time() + 604800*1000) settings = ub.session.query(ub.Settings).first() settings.config_google_drive_watch_changes_response = json.dumps(result) @@ -1585,7 +1526,7 @@ def revoke_watch_gdrive(): last_watch_response = config.config_google_drive_watch_changes_response if last_watch_response: try: - gdriveutils.stopChannel(Gdrive.Instance().drive, last_watch_response['id'], last_watch_response['resourceId']) + gdriveutils.stopChannel(gdriveutils.Gdrive.Instance().drive, last_watch_response['id'], last_watch_response['resourceId']) except HttpError: pass settings = ub.session.query(ub.Settings).first() @@ -1611,7 +1552,7 @@ def on_received_watch_confirmation(): try: j = json.loads(data) app.logger.info('Getting change details') - response = gdriveutils.getChangeById(Gdrive.Instance().drive, j['id']) + response = gdriveutils.getChangeById(gdriveutils.Gdrive.Instance().drive, j['id']) app.logger.debug(response) if response: dbpath = os.path.join(config.config_calibre_dir, "metadata.db") @@ -1620,14 +1561,14 @@ def on_received_watch_confirmation(): app.logger.info('Database file updated') copyfile(dbpath, os.path.join(tmpDir, "metadata.db_" + str(current_milli_time()))) app.logger.info('Backing up existing and downloading updated metadata.db') - gdriveutils.downloadFile(Gdrive.Instance().drive, None, "metadata.db", os.path.join(tmpDir, "tmp_metadata.db")) + gdriveutils.downloadFile(None, "metadata.db", os.path.join(tmpDir, "tmp_metadata.db")) app.logger.info('Setting up new DB') # prevent error on windows, as os.rename does on exisiting files shutil.move(os.path.join(tmpDir, "tmp_metadata.db"), dbpath) db.setup_db() except Exception as e: - app.logger.exception(e) - + app.logger.info(e.message) + # app.logger.exception(e) updateMetaData() return '' @@ -1793,19 +1734,22 @@ def advanced_search(): def get_cover_via_gdrive(cover_path): - df = gdriveutils.getFileFromEbooksFolder(Gdrive.Instance().drive, cover_path, 'cover.jpg') - if not gdriveutils.session.query(gdriveutils.PermissionAdded).filter(gdriveutils.PermissionAdded.gdrive_id == df['id']).first(): - df.GetPermissions() - df.InsertPermission({ - 'type': 'anyone', - 'value': 'anyone', - 'role': 'reader', - 'withLink': True}) - permissionAdded = gdriveutils.PermissionAdded() - permissionAdded.gdrive_id = df['id'] - gdriveutils.session.add(permissionAdded) - gdriveutils.session.commit() - return df.metadata.get('webContentLink') + df = gdriveutils.getFileFromEbooksFolder(cover_path, 'cover.jpg') + if df: + if not gdriveutils.session.query(gdriveutils.PermissionAdded).filter(gdriveutils.PermissionAdded.gdrive_id == df['id']).first(): + df.GetPermissions() + df.InsertPermission({ + 'type': 'anyone', + 'value': 'anyone', + 'role': 'reader', + 'withLink': True}) + permissionAdded = gdriveutils.PermissionAdded() + permissionAdded.gdrive_id = df['id'] + gdriveutils.session.add(permissionAdded) + gdriveutils.session.commit() + return df.metadata.get('webContentLink') + else: + return None @app.route("/cover/") @@ -1813,9 +1757,15 @@ def get_cover_via_gdrive(cover_path): def get_cover(cover_path): if config.config_use_google_drive: try: - return redirect(get_cover_via_gdrive(cover_path)) - except: - app.logger.error(cover_path + '/cover.jpg ' + 'not found on GDrive') + path=get_cover_via_gdrive(cover_path) + if path: + return redirect(path) + else: + app.logger.error(cover_path + '/cover.jpg not found on GDrive') + return send_from_directory(os.path.join(os.path.dirname(__file__), "static"), "generic_cover.jpg") + except Exception as e: + app.logger.error("Message "+e.message) + # traceback.print_exc() return send_from_directory(os.path.join(os.path.dirname(__file__), "static"),"generic_cover.jpg") else: return send_from_directory(os.path.join(config.config_calibre_dir, cover_path), "cover.jpg") @@ -1834,7 +1784,7 @@ def serve_book(book_id, book_format): headers["Content-Type"] = mimetypes.types_map['.' + book_format] except KeyError: headers["Content-Type"] = "application/octet-stream" - df = gdriveutils.getFileFromEbooksFolder(Gdrive.Instance().drive, book.path, data.name + "." + book_format) + df = gdriveutils.getFileFromEbooksFolder(book.path, data.name + "." + book_format) return do_gdrive_download(df, headers) else: return send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + book_format) @@ -1956,7 +1906,7 @@ def get_download_link(book_id, book_format): if data: # collect downloaded books only for registered user and not for anonymous user if current_user.is_authenticated: - helper.update_download(book_id, int(current_user.id)) + ub.update_download(book_id, int(current_user.id)) file_name = book.title if len(book.authors) > 0: file_name = book.authors[0].name + '_' + file_name @@ -1968,8 +1918,11 @@ def get_download_link(book_id, book_format): headers["Content-Type"] = "application/octet-stream" headers["Content-Disposition"] = "attachment; filename*=UTF-8''%s.%s" % (quote(file_name.encode('utf-8')), book_format) if config.config_use_google_drive: - df = gdriveutils.getFileFromEbooksFolder(Gdrive.Instance().drive, book.path, '%s.%s' % (data.name, book_format)) - return do_gdrive_download(df, headers) + df = gdriveutils.getFileFromEbooksFolder(book.path, '%s.%s' % (data.name, book_format)) + if df: + return do_gdrive_download(df, headers) + else: + abort(404) else: response = make_response(send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + book_format)) response.headers = headers @@ -2150,7 +2103,7 @@ def send_to_kindle(book_id): if result is None: flash(_(u"Book successfully send to %(kindlemail)s", kindlemail=current_user.kindle_mail), category="success") - helper.update_download(book_id, int(current_user.id)) + ub.update_download(book_id, int(current_user.id)) else: flash(_(u"There was an error sending this book: %(res)s", res=result), category="error") else: @@ -2398,8 +2351,9 @@ def profile(): if downloadBook: downloads.append(db.session.query(db.Books).filter(db.Books.id == book.book_id).first()) else: - ub.session.query(ub.Downloads).filter(book.book_id == ub.Downloads.book_id).delete() - ub.session.commit() + ub.delete_download(book.book_id) + # ub.session.query(ub.Downloads).filter(book.book_id == ub.Downloads.book_id).delete() + # ub.session.commit() if request.method == "POST": to_save = request.form.to_dict() content.random_books = 0 @@ -2671,7 +2625,7 @@ def configuration_helper(origin): reboot_required = True try: if content.config_use_google_drive and is_gdrive_ready() and not os.path.exists(config.config_calibre_dir + "/metadata.db"): - gdriveutils.downloadFile(Gdrive.Instance().drive, None, "metadata.db", config.config_calibre_dir + "/metadata.db") + gdriveutils.downloadFile(None, "metadata.db", config.config_calibre_dir + "/metadata.db") if db_change: if config.db_configured: db.session.close() @@ -2842,8 +2796,9 @@ def edit_user(user_id): if downloadBook: downloads.append(db.session.query(db.Books).filter(db.Books.id == book.book_id).first()) else: - ub.session.query(ub.Downloads).filter(book.book_id == ub.Downloads.book_id).delete() - ub.session.commit() + ub.delete_download(book.book_id) + # ub.session.query(ub.Downloads).filter(book.book_id == ub.Downloads.book_id).delete() + # ub.session.commit() if request.method == "POST": to_save = request.form.to_dict() if "delete" in to_save: @@ -3051,16 +3006,14 @@ def edit_book(book_id): edited_books_id.add(book.id) book.author_sort = helper.get_sorted_author(input_authors[0]) + if config.config_use_google_drive: + gdriveutils.updateGdriveCalibreFromLocal() + error = False for b in edited_books_id: - if config.config_use_google_drive: - error = helper.update_dir_structure_gdrive(b) - else: - error = helper.update_dir_stucture(b, config.config_calibre_dir) + error = helper.update_dir_stucture(b, config.config_calibre_dir) if error: # stop on error break - if config.config_use_google_drive: - updateGdriveCalibreFromLocal() if not error: if to_save["cover_url"]: @@ -3232,7 +3185,7 @@ def save_cover(url, book_path): f = open(os.path.join(tmpDir, "uploaded_cover.jpg"), "wb") f.write(img.content) f.close() - gdriveutils.uploadFileToEbooksFolder(Gdrive.Instance().drive, os.path.join(book_path, 'cover.jpg'), os.path.join(tmpDir, f.name)) + gdriveutils.uploadFileToEbooksFolder(os.path.join(book_path, 'cover.jpg'), os.path.join(tmpDir, f.name)) app.logger.info("Cover is saved on gdrive") return true @@ -3344,29 +3297,30 @@ def upload(): db.session.add(db_book) db.session.flush() # flush content get db_book.id avalible - # ToDo: Book should be moved to foldername with id in it - if config.config_use_google_drive: - error = helper.update_dir_structure_gdrive(db_book.id) - else: - error = helper.update_dir_stucture(db_book.id, config.config_calibre_dir) - # ToDo: Handle error + # add comment upload_comment = Markup(meta.description).unescape() if upload_comment != "": db.session.add(db.Comments(upload_comment, db_book.id)) - db.session.commit() - input_tags = tags.split(',') input_tags = list(map(lambda it: it.strip(), input_tags)) if input_tags[0] !="": modify_database_object(input_tags, db_book.tags, db.Tags, db.session, 'tags') + + db.session.commit() + + if config.config_use_google_drive: + gdriveutils.updateGdriveCalibreFromLocal() + error = helper.update_dir_stucture(db_book.id, config.config_calibre_dir) + # ToDo: Handle error + if error: + pass + if db_language is not None: # display Full name instead of iso639.part3 db_book.languages[0].language_name = _(meta.languages) author_names = [] for author in db_book.authors: author_names.append(author.name) - if config.config_use_google_drive: - updateGdriveCalibreFromLocal() if len(request.files.getlist("btn-upload")) < 2: cc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() if current_user.role_edit() or current_user.role_admin():