Commit 204cd504 authored by OzzieIsaacs's avatar OzzieIsaacs

- Completly new setup of GDrive (every data is taken from client_secret.json file)

- If cover on Google Drive is missing, the generic cover will be used
- Better error handling during configuration of google drive (missing hook callback verification, missing files)
- less errors on console during use of Gdrive, more log file output in case of error
- removed standalone flask development mode
- removed gdrive template file and vendor setup script
parent 43a391d1
......@@ -21,14 +21,11 @@ except ImportError:
gevent_present = False
if __name__ == '__main__':
if web.ub.DEVELOPMENT:
web.app.run(port=web.ub.config.config_port, debug=True)
else:
if gevent_present:
web.app.logger.info('Attempting to start gevent')
web.start_gevent()
else:
web.app.logger.info('Falling back to Tornado')
web.app.logger.info('Starting Tornado webserver')
# Max Buffersize set to 200MB
if web.ub.config.get_config_certfile() and web.ub.config.get_config_keyfile():
ssl={"certfile": web.ub.config.get_config_certfile(),
......@@ -42,7 +39,6 @@ if __name__ == '__main__':
IOLoop.instance().start()
IOLoop.instance().close(True)
if web.helper.global_task == 0:
web.app.logger.info("Performing restart of Calibre-web")
if os.name == 'nt':
......
......@@ -68,7 +68,6 @@ if not os.path.exists(cli.gdpath):
Base.metadata.create_all(engine)
except Exception:
raise
migrate()
......@@ -80,7 +79,10 @@ def getDrive(drive=None, gauth=None):
gauth.LoadCredentialsFile("gdrive_credentials")
if gauth.access_token_expired:
# Refresh them if expired
try:
gauth.Refresh()
except:
web.app.logger.error("GDrive gdrive_credentials file not present, reauthenticate in config section")
else:
# Initialize the saved creds
gauth.Authorize()
......@@ -90,12 +92,24 @@ def getDrive(drive=None, gauth=None):
drive.auth.Refresh()
return drive
def listRootFolders(drive=None):
drive = getDrive(drive)
folder = "'root' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false"
fileList = drive.ListFile({'q': folder}).GetList()
return fileList
def getEbooksFolder(drive=None):
drive = getDrive(drive)
ebooksFolder = "title = '%s' and 'root' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % config.config_google_drive_folder
return getFolderInFolder('root',config.config_google_drive_folder,drive)
fileList = drive.ListFile({'q': ebooksFolder}).GetList()
def getFolderInFolder(parentId, folderName,drive=None):
drive = getDrive(drive)
query=""
if folderName:
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]
......@@ -105,20 +119,17 @@ def getEbooksFolderId(drive=None):
return storedPathName.gdrive_id
else:
gDriveId = GdriveId()
try:
gDriveId.gdrive_id = getEbooksFolder(drive)['id']
except:
pass
# ToDo Path not exisiting
gDriveId.path = '/'
session.merge(gDriveId)
session.commit()
return
def getFolderInFolder(parentId, folderName, drive=None):
drive = getDrive(drive)
folder = "title = '%s' and '%s' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % (folderName.replace("'", "\\'"), parentId)
fileList = drive.ListFile({'q': folder}).GetList()
return fileList[0]
def getFile(pathId, fileName, drive=None):
drive = getDrive(drive)
metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'"))
......@@ -190,11 +201,8 @@ def downloadFile(drive, path, filename, output):
def backupCalibreDbAndOptionalDownload(drive, f=None):
drive = getDrive(drive)
metaDataFile = "'%s' in parents and title = 'metadata.db' and trashed = false" % getEbooksFolderId()
fileList = drive.ListFile({'q': metaDataFile}).GetList()
databaseFile = fileList[0]
if f:
databaseFile.GetContentFile(f)
......@@ -343,3 +351,8 @@ def getChangeById (drive, change_id):
except (errors.HttpError, error):
web.app.logger.exception(error)
return None
# Deletes the local hashes database to force search for new folder names
def deleteDatabaseOnChange():
session.query(GdriveId).delete()
session.commit()
......@@ -7,47 +7,43 @@
<label for="config_calibre_dir">{{_('Location of Calibre database')}}</label>
<input type="text" class="form-control" name="config_calibre_dir" id="config_calibre_dir" value="{% if content.config_calibre_dir != None %}{{ content.config_calibre_dir }}{% endif %}" autocomplete="off">
</div>
{% if gdrive %}
<div class="form-group required">
<input type="checkbox" id="config_use_google_drive" name="config_use_google_drive" data-control="gdrive_settings" {% if content.config_use_google_drive %}checked{% endif %} >
<label for="config_use_google_drive">{{_('Use google drive?')}}</label>
</div>
<div data-related="gdrive_settings">
<div class="form-group required">
<label for="config_google_drive_client_id">{{_('Client id')}}</label>
<input type="text" class="form-control" name="config_google_drive_client_id" id="config_google_drive_client_id" value="{% if content.config_google_drive_client_id %}{{content.config_google_drive_client_id}}{% endif %}" autocomplete="off">
</div>
<div class="form-group required">
<label for="config_google_drive_client_secret">{{_('Client secret')}}</label>
<input type="text" class="form-control" name="config_google_drive_client_secret" id="config_google_drive_client_secret" value="{% if content.config_google_drive_client_secret %}{{content.config_google_drive_client_secret}}{% endif %}" autocomplete="off">
{% if gdriveError %}
<div class="form-group">
<label>
{{_('Google drive config problem')}}: {{ gdriveError }}
</label>
</div>
{% else %}
{% if show_authenticate_google_drive %}
<div class="form-group required">
<label for="config_google_drive_calibre_url_base">{{_('Calibre-web Base URL')}}</label>
<input type="text" class="form-control" name="config_google_drive_calibre_url_base" id="config_google_drive_calibre_url_base" value="{% if content.config_google_drive_calibre_url_base %}{{content.config_google_drive_calibre_url_base}}{% endif %}" autocomplete="off">
<a href="{{ url_for('authenticate_google_drive') }}" class="btn btn-primary">{{_('Authenticate Google Drive')}}</a>
</div>
{% else %}
<div class="form-group required">
<label for="config_google_drive_folder">{{_('Google drive Calibre folder')}}</label>
<input type="text" class="form-control" name="config_google_drive_folder" id="config_google_drive_folder" value="{% if content.config_google_drive_folder %}{{content.config_google_drive_folder}}{% endif %}" autocomplete="off" required>
</div>
{% if show_authenticate_google_drive %}
<div class="form-group required">
<a href="{{ url_for('authenticate_google_drive') }}" class="btn btn-primary">Authenticate Google Drive</a>
<select name="config_google_drive_folder" id="config_google_drive_folder" class="form-control">
{% for gdrivefolder in gdrivefolders %}
<option value="{{ gdrivefolder.title }}" {% if gdrivefolder.title == content.config_google_drive_folder %}selected{% endif %}>{{ gdrivefolder.title }}</option>
{% endfor %}
</select>
</div>
{% else %}
{% if content.config_google_drive_watch_changes_response %}
<label for="config_google_drive_watch_changes_response">{{_('Metadata Watch Channel ID')}}</label>
<div class="form-group input-group required">
<input type="text" class="form-control" name="config_google_drive_watch_changes_response" id="config_google_drive_watch_changes_response" value="{{ content.config_google_drive_watch_changes_response['id'] }} expires on {{ content.config_google_drive_watch_changes_response['expiration'] | strftime }}" autocomplete="off" disabled="">
<span class="input-group-btn">
<a href="{{ url_for('revoke_watch_gdrive') }}" class="btn btn-primary">Revoke</a>
</span>
<span class="input-group-btn"><a href="{{ url_for('revoke_watch_gdrive') }}" class="btn btn-primary">{{_('Revoke')}}</a></span>
</div>
{% else %}
<a href="{{ url_for('watch_gdrive') }}" class="btn btn-primary">Enable watch of metadata.db</a>
{% endif %}
</div>
{% endif %}
</div>
{% endif %}
</div>
<div class="form-group">
<label for="config_port">{{_('Server Port')}}</label>
<input type="number" min="1" max="65535" class="form-control" name="config_port" id="config_port" value="{% if content.config_port != None %}{{ content.config_port }}{% endif %}" autocomplete="off" required>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -291,10 +291,10 @@ class Settings(Base):
config_default_show = Column(SmallInteger, default=2047)
config_columns_to_ignore = Column(String)
config_use_google_drive = Column(Boolean)
config_google_drive_client_id = Column(String)
config_google_drive_client_secret = Column(String)
# config_google_drive_client_id = Column(String)
# config_google_drive_client_secret = Column(String)
config_google_drive_folder = Column(String)
config_google_drive_calibre_url_base = Column(String)
# config_google_drive_calibre_url_base = Column(String)
config_google_drive_watch_changes_response = Column(String)
config_remote_login = Column(Boolean)
config_use_goodreads = Column(Boolean)
......@@ -352,9 +352,9 @@ class Config:
self.config_default_show = data.config_default_show
self.config_columns_to_ignore = data.config_columns_to_ignore
self.config_use_google_drive = data.config_use_google_drive
self.config_google_drive_client_id = data.config_google_drive_client_id
self.config_google_drive_client_secret = data.config_google_drive_client_secret
self.config_google_drive_calibre_url_base = data.config_google_drive_calibre_url_base
# self.config_google_drive_client_id = data.config_google_drive_client_id
# self.config_google_drive_client_secret = data.config_google_drive_client_secret
# self.config_google_drive_calibre_url_base = data.config_google_drive_calibre_url_base
self.config_google_drive_folder = data.config_google_drive_folder
if data.config_google_drive_watch_changes_response:
self.config_google_drive_watch_changes_response = json.loads(data.config_google_drive_watch_changes_response)
......@@ -550,9 +550,9 @@ def migrate_Database():
except exc.OperationalError:
conn = engine.connect()
conn.execute("ALTER TABLE Settings ADD column `config_use_google_drive` INTEGER DEFAULT 0")
conn.execute("ALTER TABLE Settings ADD column `config_google_drive_client_id` String DEFAULT ''")
conn.execute("ALTER TABLE Settings ADD column `config_google_drive_client_secret` String DEFAULT ''")
conn.execute("ALTER TABLE Settings ADD column `config_google_drive_calibre_url_base` INTEGER DEFAULT 0")
# conn.execute("ALTER TABLE Settings ADD column `config_google_drive_client_id` String DEFAULT ''")
# conn.execute("ALTER TABLE Settings ADD column `config_google_drive_client_secret` String DEFAULT ''")
# conn.execute("ALTER TABLE Settings ADD column `config_google_drive_calibre_url_base` INTEGER DEFAULT 0")
conn.execute("ALTER TABLE Settings ADD column `config_google_drive_folder` String DEFAULT ''")
conn.execute("ALTER TABLE Settings ADD column `config_google_drive_watch_changes_response` String DEFAULT ''")
try:
......
This diff is collapsed.
client_config_backend: settings
client_config:
client_id: %(client_id)s
client_secret: %(client_secret)s
redirect_uri: %(redirect_uri)s
save_credentials: True
save_credentials_backend: file
save_credentials_file: gdrive_credentials
get_refresh_token: True
oauth_scope:
- https://www.googleapis.com/auth/drive
#!/bin/bash -e
pip install --target ./vendor -r requirements.txt
This diff is collapsed.
gevent==1.2.1
# GDrive Integration
google-api-python-client==1.6.1
gevent==1.2.1
greenlet==0.4.12
httplib2==0.9.2
lxml==3.7.2
oauth2client==4.0.0
uritemplate==3.0.0
pyasn1-modules==0.0.8
pyasn1==0.1.9
PyDrive==1.3.1
PyYAML==3.12
rsa==3.4.2
six==1.10.0
uritemplate==3.0.0
# goodreads
goodreads>=0.3.2
python-Levenshtein>=0.12.0
# other
lxml==3.7.2
......@@ -88,22 +88,22 @@ Once a project has been created, we need to create a client ID and a client secr
The Drive API should now be setup and ready to use, so we need to integrate it into Calibre-Web. This is done as below: -
1. Open config page
2. Enter the location that will be used to store the metadata.db file, and to temporary store uploaded books and other temporary files for upload
2. Enter the location that will be used to store the metadata.db file locally, and to temporary store uploaded books and other temporary files for upload ("Location of Calibre database")
2. Tick Use Google Drive
3. Enter Client Secret and Client Key as provided via previous steps
4. Enter the folder that is the root of your calibre library
5. Enter base URL for calibre-web (used for google callbacks)
6. Click the "Submit" button
7. Come back to the configuration form
8. Now select Authenticate Google Drive
9. This should redirect you to google to allow it top use your Drive, and then redirect you back to the config page
10. Google Drive should now be connected and be used to get images and download Epubs. The metadata.db is stored in the calibre library location
3. Click the "Submit" button
4. Now select Authenticate Google Drive
5. This should redirect you to Google to allow it top use your Drive, and then redirect you back to the config page
6. Select the folder that is the root of your calibre library on Gdrive ("Google drive Calibre folder")
7. Click the "Submit" button
8. Google Drive should now be connected and be used to get images and download Epubs. The metadata.db is stored in the calibre library location
### Optional
If your calibre web is using https, it is possible to add a "watch" to the drive. This will inform us if the metadata.db file is updated and allow us to update our calibre library accordingly.
Additionally the public adress your server uses (e.g.https://example.com) has to be verified in the Google developer console. After this is done, please wait a few minutes.
11. Click enable watch of metadata.db
12. Note that this expires after a week, so will need to be manually refresh
9. Open config page
10. Click enable watch of metadata.db
11. Note that this expires after a week, so will need to be manually refresh
## Docker image
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment