mirror of
https://github.com/janeczku/calibre-web
synced 2024-12-18 14:10:30 +00:00
Added Scheduled Tasks Settings
This commit is contained in:
parent
0bd544704d
commit
26071d4e7a
62
cps/admin.py
62
cps/admin.py
@ -159,23 +159,6 @@ def shutdown():
|
|||||||
return json.dumps(showtext), 400
|
return json.dumps(showtext), 400
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/clear-cache")
|
|
||||||
@login_required
|
|
||||||
@admin_required
|
|
||||||
def clear_cache():
|
|
||||||
cache_type = request.args.get('cache_type'.strip())
|
|
||||||
showtext = {}
|
|
||||||
|
|
||||||
if cache_type == constants.CACHE_TYPE_THUMBNAILS:
|
|
||||||
log.info('clearing cover thumbnail cache')
|
|
||||||
showtext['text'] = _(u'Cleared cover thumbnail cache')
|
|
||||||
helper.clear_cover_thumbnail_cache()
|
|
||||||
return json.dumps(showtext)
|
|
||||||
|
|
||||||
showtext['text'] = _(u'Unknown command')
|
|
||||||
return json.dumps(showtext)
|
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/view")
|
@admi.route("/admin/view")
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
@ -205,6 +188,7 @@ def admin():
|
|||||||
feature_support=feature_support, kobo_support=kobo_support,
|
feature_support=feature_support, kobo_support=kobo_support,
|
||||||
title=_(u"Admin page"), page="admin")
|
title=_(u"Admin page"), page="admin")
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/dbconfig", methods=["GET", "POST"])
|
@admi.route("/admin/dbconfig", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
@ -245,6 +229,7 @@ def ajax_db_config():
|
|||||||
def calibreweb_alive():
|
def calibreweb_alive():
|
||||||
return "", 200
|
return "", 200
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/viewconfig")
|
@admi.route("/admin/viewconfig")
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
@ -257,6 +242,7 @@ def view_configuration():
|
|||||||
restrictColumns=restrict_columns,
|
restrictColumns=restrict_columns,
|
||||||
title=_(u"UI Configuration"), page="uiconfig")
|
title=_(u"UI Configuration"), page="uiconfig")
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/usertable")
|
@admi.route("/admin/usertable")
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
@ -339,6 +325,7 @@ def list_users():
|
|||||||
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/ajax/deleteuser", methods=['POST'])
|
@admi.route("/ajax/deleteuser", methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
@ -372,6 +359,7 @@ def delete_user():
|
|||||||
success.extend(errors)
|
success.extend(errors)
|
||||||
return Response(json.dumps(success), mimetype='application/json')
|
return Response(json.dumps(success), mimetype='application/json')
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/ajax/getlocale")
|
@admi.route("/ajax/getlocale")
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
@ -517,6 +505,7 @@ def update_table_settings():
|
|||||||
return "Invalid request", 400
|
return "Invalid request", 400
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def check_valid_read_column(column):
|
def check_valid_read_column(column):
|
||||||
if column != "0":
|
if column != "0":
|
||||||
if not calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.id == column) \
|
if not calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.id == column) \
|
||||||
@ -524,6 +513,7 @@ def check_valid_read_column(column):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def check_valid_restricted_column(column):
|
def check_valid_restricted_column(column):
|
||||||
if column != "0":
|
if column != "0":
|
||||||
if not calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.id == column) \
|
if not calibre_db.session.query(db.Custom_Columns).filter(db.Custom_Columns.id == column) \
|
||||||
@ -532,7 +522,6 @@ def check_valid_restricted_column(column):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/viewconfig", methods=["POST"])
|
@admi.route("/admin/viewconfig", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
@ -564,7 +553,6 @@ def update_view_configuration():
|
|||||||
_config_int(to_save, "config_books_per_page")
|
_config_int(to_save, "config_books_per_page")
|
||||||
_config_int(to_save, "config_authors_max")
|
_config_int(to_save, "config_authors_max")
|
||||||
|
|
||||||
|
|
||||||
config.config_default_role = constants.selected_roles(to_save)
|
config.config_default_role = constants.selected_roles(to_save)
|
||||||
config.config_default_role &= ~constants.ROLE_ANONYMOUS
|
config.config_default_role &= ~constants.ROLE_ANONYMOUS
|
||||||
|
|
||||||
@ -1210,6 +1198,7 @@ def _db_configuration_update_helper():
|
|||||||
config.save()
|
config.save()
|
||||||
return _db_configuration_result(None, gdrive_error)
|
return _db_configuration_result(None, gdrive_error)
|
||||||
|
|
||||||
|
|
||||||
def _configuration_update_helper():
|
def _configuration_update_helper():
|
||||||
reboot_required = False
|
reboot_required = False
|
||||||
to_save = request.form.to_dict()
|
to_save = request.form.to_dict()
|
||||||
@ -1299,6 +1288,7 @@ def _configuration_update_helper():
|
|||||||
|
|
||||||
return _configuration_result(None, reboot_required)
|
return _configuration_result(None, reboot_required)
|
||||||
|
|
||||||
|
|
||||||
def _configuration_result(error_flash=None, reboot=False):
|
def _configuration_result(error_flash=None, reboot=False):
|
||||||
resp = {}
|
resp = {}
|
||||||
if error_flash:
|
if error_flash:
|
||||||
@ -1388,6 +1378,7 @@ def _handle_new_user(to_save, content, languages, translations, kobo_support):
|
|||||||
log.error("Settings DB is not Writeable")
|
log.error("Settings DB is not Writeable")
|
||||||
flash(_("Settings DB is not Writeable"), category="error")
|
flash(_("Settings DB is not Writeable"), category="error")
|
||||||
|
|
||||||
|
|
||||||
def _delete_user(content):
|
def _delete_user(content):
|
||||||
if ub.session.query(ub.User).filter(ub.User.role.op('&')(constants.ROLE_ADMIN) == constants.ROLE_ADMIN,
|
if ub.session.query(ub.User).filter(ub.User.role.op('&')(constants.ROLE_ADMIN) == constants.ROLE_ADMIN,
|
||||||
ub.User.id != content.id).count():
|
ub.User.id != content.id).count():
|
||||||
@ -1572,6 +1563,39 @@ def update_mailsettings():
|
|||||||
return edit_mailsettings()
|
return edit_mailsettings()
|
||||||
|
|
||||||
|
|
||||||
|
@admi.route("/admin/scheduledtasks")
|
||||||
|
@login_required
|
||||||
|
@admin_required
|
||||||
|
def edit_scheduledtasks():
|
||||||
|
content = config.get_scheduled_task_settings()
|
||||||
|
return render_title_template("schedule_edit.html", content=content, title=_(u"Edit Scheduled Tasks Settings"))
|
||||||
|
|
||||||
|
|
||||||
|
@admi.route("/admin/scheduledtasks", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
@admin_required
|
||||||
|
def update_scheduledtasks():
|
||||||
|
to_save = request.form.to_dict()
|
||||||
|
_config_int(to_save, "schedule_start_time")
|
||||||
|
_config_int(to_save, "schedule_end_time")
|
||||||
|
_config_checkbox(to_save, "schedule_generate_book_covers")
|
||||||
|
_config_checkbox(to_save, "schedule_generate_series_covers")
|
||||||
|
|
||||||
|
try:
|
||||||
|
config.save()
|
||||||
|
flash(_(u"Scheduled tasks settings updated"), category="success")
|
||||||
|
except IntegrityError as ex:
|
||||||
|
ub.session.rollback()
|
||||||
|
log.error("An unknown error occurred while saving scheduled tasks settings")
|
||||||
|
flash(_(u"An unknown error occurred. Please try again later."), category="error")
|
||||||
|
except OperationalError:
|
||||||
|
ub.session.rollback()
|
||||||
|
log.error("Settings DB is not Writeable")
|
||||||
|
flash(_("Settings DB is not Writeable"), category="error")
|
||||||
|
|
||||||
|
return edit_scheduledtasks()
|
||||||
|
|
||||||
|
|
||||||
@admi.route("/admin/user/<int:user_id>", methods=["GET", "POST"])
|
@admi.route("/admin/user/<int:user_id>", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
|
@ -133,13 +133,18 @@ class _Settings(_Base):
|
|||||||
config_calibre = Column(String)
|
config_calibre = Column(String)
|
||||||
config_rarfile_location = Column(String, default=None)
|
config_rarfile_location = Column(String, default=None)
|
||||||
config_upload_formats = Column(String, default=','.join(constants.EXTENSIONS_UPLOAD))
|
config_upload_formats = Column(String, default=','.join(constants.EXTENSIONS_UPLOAD))
|
||||||
config_unicode_filename =Column(Boolean, default=False)
|
config_unicode_filename = Column(Boolean, default=False)
|
||||||
|
|
||||||
config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE)
|
config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE)
|
||||||
|
|
||||||
config_reverse_proxy_login_header_name = Column(String)
|
config_reverse_proxy_login_header_name = Column(String)
|
||||||
config_allow_reverse_proxy_header_login = Column(Boolean, default=False)
|
config_allow_reverse_proxy_header_login = Column(Boolean, default=False)
|
||||||
|
|
||||||
|
schedule_start_time = Column(Integer, default=4)
|
||||||
|
schedule_end_time = Column(Integer, default=6)
|
||||||
|
schedule_generate_book_covers = Column(Boolean, default=False)
|
||||||
|
schedule_generate_series_covers = Column(Boolean, default=False)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__class__.__name__
|
return self.__class__.__name__
|
||||||
|
|
||||||
@ -170,7 +175,6 @@ class _ConfigSQL(object):
|
|||||||
if change:
|
if change:
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
||||||
def _read_from_storage(self):
|
def _read_from_storage(self):
|
||||||
if self._settings is None:
|
if self._settings is None:
|
||||||
log.debug("_ConfigSQL._read_from_storage")
|
log.debug("_ConfigSQL._read_from_storage")
|
||||||
@ -254,6 +258,8 @@ class _ConfigSQL(object):
|
|||||||
return bool((self.mail_server != constants.DEFAULT_MAIL_SERVER and self.mail_server_type == 0)
|
return bool((self.mail_server != constants.DEFAULT_MAIL_SERVER and self.mail_server_type == 0)
|
||||||
or (self.mail_gmail_token != {} and self.mail_server_type == 1))
|
or (self.mail_gmail_token != {} and self.mail_server_type == 1))
|
||||||
|
|
||||||
|
def get_scheduled_task_settings(self):
|
||||||
|
return {k:v for k, v in self.__dict__.items() if k.startswith('schedule_')}
|
||||||
|
|
||||||
def set_from_dictionary(self, dictionary, field, convertor=None, default=None, encode=None):
|
def set_from_dictionary(self, dictionary, field, convertor=None, default=None, encode=None):
|
||||||
"""Possibly updates a field of this object.
|
"""Possibly updates a field of this object.
|
||||||
@ -289,7 +295,6 @@ class _ConfigSQL(object):
|
|||||||
storage[k] = v
|
storage[k] = v
|
||||||
return storage
|
return storage
|
||||||
|
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
'''Load all configuration values from the underlying storage.'''
|
'''Load all configuration values from the underlying storage.'''
|
||||||
s = self._read_from_storage() # type: _Settings
|
s = self._read_from_storage() # type: _Settings
|
||||||
@ -407,6 +412,7 @@ def autodetect_calibre_binary():
|
|||||||
return element
|
return element
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def autodetect_unrar_binary():
|
def autodetect_unrar_binary():
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
calibre_path = ["C:\\program files\\WinRar\\unRAR.exe",
|
calibre_path = ["C:\\program files\\WinRar\\unRAR.exe",
|
||||||
@ -418,6 +424,7 @@ def autodetect_unrar_binary():
|
|||||||
return element
|
return element
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def autodetect_kepubify_binary():
|
def autodetect_kepubify_binary():
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
calibre_path = ["C:\\program files\\kepubify\\kepubify-windows-64Bit.exe",
|
calibre_path = ["C:\\program files\\kepubify\\kepubify-windows-64Bit.exe",
|
||||||
@ -429,6 +436,7 @@ def autodetect_kepubify_binary():
|
|||||||
return element
|
return element
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _migrate_database(session):
|
def _migrate_database(session):
|
||||||
# make sure the table is created, if it does not exist
|
# make sure the table is created, if it does not exist
|
||||||
_Base.metadata.create_all(session.bind)
|
_Base.metadata.create_all(session.bind)
|
||||||
@ -452,6 +460,7 @@ def load_configuration(session):
|
|||||||
# session.commit()
|
# session.commit()
|
||||||
return conf
|
return conf
|
||||||
|
|
||||||
|
|
||||||
def get_flask_session_key(session):
|
def get_flask_session_key(session):
|
||||||
flask_settings = session.query(_Flask_Settings).one_or_none()
|
flask_settings = session.query(_Flask_Settings).one_or_none()
|
||||||
if flask_settings == None:
|
if flask_settings == None:
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
from __future__ import division, print_function, unicode_literals
|
from __future__ import division, print_function, unicode_literals
|
||||||
|
|
||||||
from .services.background_scheduler import BackgroundScheduler
|
from .services.background_scheduler import BackgroundScheduler
|
||||||
from .services.worker import WorkerThread
|
|
||||||
from .tasks.database import TaskReconnectDatabase
|
from .tasks.database import TaskReconnectDatabase
|
||||||
from .tasks.thumbnail import TaskGenerateCoverThumbnails, TaskGenerateSeriesThumbnails
|
from .tasks.thumbnail import TaskGenerateCoverThumbnails, TaskGenerateSeriesThumbnails
|
||||||
|
|
||||||
@ -28,13 +27,19 @@ def register_jobs():
|
|||||||
scheduler = BackgroundScheduler()
|
scheduler = BackgroundScheduler()
|
||||||
|
|
||||||
if scheduler:
|
if scheduler:
|
||||||
# Reconnect metadata.db once every 12 hours
|
# Reconnect Calibre database (metadata.db)
|
||||||
scheduler.add_task(user=None, task=lambda: TaskReconnectDatabase(), trigger='cron', hour='4,16')
|
scheduler.schedule_task(user=None, task=lambda: TaskReconnectDatabase(), trigger='cron', hour='4,16')
|
||||||
|
|
||||||
# Generate all missing book cover thumbnails once every 24 hours
|
# Generate all missing book cover thumbnails
|
||||||
scheduler.add_task(user=None, task=lambda: TaskGenerateCoverThumbnails(), trigger='cron', hour=4)
|
scheduler.schedule_task(user=None, task=lambda: TaskGenerateCoverThumbnails(), trigger='cron', hour=4)
|
||||||
|
|
||||||
|
# Generate all missing series thumbnails
|
||||||
|
scheduler.schedule_task(user=None, task=lambda: TaskGenerateSeriesThumbnails(), trigger='cron', hour=4)
|
||||||
|
|
||||||
|
|
||||||
def register_startup_jobs():
|
def register_startup_jobs():
|
||||||
WorkerThread.add(None, TaskGenerateCoverThumbnails())
|
scheduler = BackgroundScheduler()
|
||||||
# WorkerThread.add(None, TaskGenerateSeriesThumbnails())
|
|
||||||
|
if scheduler:
|
||||||
|
scheduler.schedule_task_immediately(None, task=lambda: TaskGenerateCoverThumbnails())
|
||||||
|
scheduler.schedule_task_immediately(None, task=lambda: TaskGenerateSeriesThumbnails())
|
||||||
|
@ -40,25 +40,31 @@ class BackgroundScheduler:
|
|||||||
|
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super(BackgroundScheduler, cls).__new__(cls)
|
cls._instance = super(BackgroundScheduler, cls).__new__(cls)
|
||||||
|
|
||||||
scheduler = BScheduler()
|
|
||||||
atexit.register(lambda: scheduler.shutdown())
|
|
||||||
|
|
||||||
cls.log = logger.create()
|
cls.log = logger.create()
|
||||||
cls.scheduler = scheduler
|
cls.scheduler = BScheduler()
|
||||||
cls.scheduler.start()
|
cls.scheduler.start()
|
||||||
|
|
||||||
|
atexit.register(lambda: cls.scheduler.shutdown())
|
||||||
|
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def add(self, func, trigger, **trigger_args):
|
def _add(self, func, trigger, **trigger_args):
|
||||||
if use_APScheduler:
|
if use_APScheduler:
|
||||||
return self.scheduler.add_job(func=func, trigger=trigger, **trigger_args)
|
return self.scheduler.add_job(func=func, trigger=trigger, **trigger_args)
|
||||||
|
|
||||||
def add_task(self, user, task, trigger, **trigger_args):
|
# Expects a lambda expression for the task, so that the task isn't instantiated before the task is scheduled
|
||||||
|
def schedule_task(self, user, task, trigger, **trigger_args):
|
||||||
if use_APScheduler:
|
if use_APScheduler:
|
||||||
def scheduled_task():
|
def scheduled_task():
|
||||||
worker_task = task()
|
worker_task = task()
|
||||||
self.log.info(f'Running scheduled task in background: {worker_task.name} - {worker_task.message}')
|
|
||||||
WorkerThread.add(user, worker_task)
|
WorkerThread.add(user, worker_task)
|
||||||
|
|
||||||
return self.add(func=scheduled_task, trigger=trigger, **trigger_args)
|
return self._add(func=scheduled_task, trigger=trigger, **trigger_args)
|
||||||
|
|
||||||
|
# Expects a lambda expression for the task, so that the task isn't instantiated before the task is scheduled
|
||||||
|
def schedule_task_immediately(self, user, task):
|
||||||
|
if use_APScheduler:
|
||||||
|
def scheduled_task():
|
||||||
|
WorkerThread.add(user, task())
|
||||||
|
|
||||||
|
return self._add(func=scheduled_task, trigger='date')
|
||||||
|
@ -167,9 +167,9 @@ class TaskGenerateCoverThumbnails(CalibreTask):
|
|||||||
try:
|
try:
|
||||||
stream = urlopen(web_content_link)
|
stream = urlopen(web_content_link)
|
||||||
with Image(file=stream) as img:
|
with Image(file=stream) as img:
|
||||||
height = self.get_thumbnail_height(thumbnail)
|
height = get_resize_height(thumbnail.resolution)
|
||||||
if img.height > height:
|
if img.height > height:
|
||||||
width = self.get_thumbnail_width(height, img)
|
width = get_resize_width(thumbnail.resolution, img.width, img.height)
|
||||||
img.resize(width=width, height=height, filter='lanczos')
|
img.resize(width=width, height=height, filter='lanczos')
|
||||||
img.format = thumbnail.format
|
img.format = thumbnail.format
|
||||||
filename = self.cache.get_cache_file_path(thumbnail.filename,
|
filename = self.cache.get_cache_file_path(thumbnail.filename,
|
||||||
@ -212,16 +212,6 @@ class TaskGenerateSeriesThumbnails(CalibreTask):
|
|||||||
constants.COVER_THUMBNAIL_MEDIUM
|
constants.COVER_THUMBNAIL_MEDIUM
|
||||||
]
|
]
|
||||||
|
|
||||||
# get all series
|
|
||||||
# get all books in series with covers and count >= 4 books
|
|
||||||
# get the dimensions from the first book in the series & pop the first book from the series list of books
|
|
||||||
# randomly select three other books in the series
|
|
||||||
|
|
||||||
# resize the covers in the sequence?
|
|
||||||
# create an image sequence from the 4 selected books of the series
|
|
||||||
# join pairs of books in the series with wand's concat
|
|
||||||
# join the two sets of pairs with wand's
|
|
||||||
|
|
||||||
def run(self, worker_thread):
|
def run(self, worker_thread):
|
||||||
if self.calibre_db.session and use_IM:
|
if self.calibre_db.session and use_IM:
|
||||||
all_series = self.get_series_with_four_plus_books()
|
all_series = self.get_series_with_four_plus_books()
|
||||||
|
@ -156,6 +156,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<h2>{{_('Scheduled Tasks')}}</h2>
|
||||||
|
<div class="col-xs-12 col-sm-12">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6 col-sm-3">{{_('Time at which tasks start to run')}}</div>
|
||||||
|
<div class="col-xs-6 col-sm-3">{{config.schedule_start_time}}:00</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6 col-sm-3">{{_('Time at which tasks stop running')}}</div>
|
||||||
|
<div class="col-xs-6 col-sm-3">{{config.schedule_end_time}}:00</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6 col-sm-3">{{_('Generate book cover thumbnails')}}</div>
|
||||||
|
<div class="col-xs-6 col-sm-3">{{ display_bool_setting(config.schedule_generate_book_covers) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-6 col-sm-3">{{_('Generate series cover thumbnails')}}</div>
|
||||||
|
<div class="col-xs-6 col-sm-3">{{ display_bool_setting(config.schedule_generate_series_covers) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a class="btn btn-default scheduledtasks" id="admin_edit_scheduled_tasks" href="{{url_for('admin.edit_scheduledtasks')}}">{{_('Edit Scheduled Tasks Settings')}}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<h2>{{_('Administration')}}</h2>
|
<h2>{{_('Administration')}}</h2>
|
||||||
<a class="btn btn-default" id="debug" href="{{url_for('admin.download_debug')}}">{{_('Download Debug Package')}}</a>
|
<a class="btn btn-default" id="debug" href="{{url_for('admin.download_debug')}}">{{_('Download Debug Package')}}</a>
|
||||||
@ -163,7 +188,6 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<div class="btn btn-default" id="restart_database" data-toggle="modal" data-target="#StatusDialog">{{_('Reconnect Calibre Database')}}</div>
|
<div class="btn btn-default" id="restart_database" data-toggle="modal" data-target="#StatusDialog">{{_('Reconnect Calibre Database')}}</div>
|
||||||
<div class="btn btn-default" id="clear_cover_thumbnail_cache" data-toggle="modal" data-target="#ClearCacheDialog">{{_('Clear Cover Thumbnail Cache')}}</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<div class="btn btn-default" id="admin_restart" data-toggle="modal" data-target="#RestartDialog">{{_('Restart')}}</div>
|
<div class="btn btn-default" id="admin_restart" data-toggle="modal" data-target="#RestartDialog">{{_('Restart')}}</div>
|
||||||
@ -248,21 +272,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="ClearCacheDialog" class="modal fade" role="dialog">
|
|
||||||
<div class="modal-dialog modal-sm">
|
|
||||||
<!-- Modal content-->
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header bg-info"></div>
|
|
||||||
<div class="modal-body text-center">
|
|
||||||
<p>{{_('Are you sure you want to clear the cover thumbnail cache?')}}</p>
|
|
||||||
<div id="spinner3" class="spinner" style="display:none;">
|
|
||||||
<img id="img-spinner3" src="{{ url_for('static', filename='css/libs/images/loading-icon.gif') }}"/>
|
|
||||||
</div>
|
|
||||||
<p></p>
|
|
||||||
<button type="button" class="btn btn-default" id="clear_cache" >{{_('OK')}}</button>
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Cancel')}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
38
cps/templates/schedule_edit.html
Normal file
38
cps/templates/schedule_edit.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{% extends "layout.html" %}
|
||||||
|
{% block header %}
|
||||||
|
<link href="{{ url_for('static', filename='css/libs/bootstrap-table.min.css') }}" rel="stylesheet">
|
||||||
|
<link href="{{ url_for('static', filename='css/libs/bootstrap-editable.css') }}" rel="stylesheet">
|
||||||
|
{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="discover">
|
||||||
|
<h1>{{title}}</h1>
|
||||||
|
<form role="form" class="col-md-10 col-lg-6" method="POST" autocomplete="off">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="schedule_start_time">{{_('Time at which tasks start to run')}}</label>
|
||||||
|
<select name="schedule_start_time" id="schedule_start_time" class="form-control">
|
||||||
|
{% for n in range(24) %}
|
||||||
|
<option value="{{n}}" {% if content.schedule_start_time == n %}selected{% endif %}>{{n}}{{_(':00')}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="schedule_end_time">{{_('Time at which tasks stop running')}}</label>
|
||||||
|
<select name="schedule_end_time" id="schedule_end_time" class="form-control">
|
||||||
|
{% for n in range(24) %}
|
||||||
|
<option value="{{n}}" {% if content.schedule_end_time == n %}selected{% endif %}>{{n}}{{_(':00')}}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="checkbox" id="schedule_generate_book_covers" name="schedule_generate_book_covers" checked>
|
||||||
|
<label for="schedule_generate_book_covers">{{_('Generate Book Cover Thumbnails')}}</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="checkbox" id="schedule_generate_series_covers" name="schedule_generate_series_covers" {% if config.schedule_generate_series_covers %}checked{% endif %}>
|
||||||
|
<label for="schedule_generate_series_covers">{{_('Generate Series Cover Thumbnails')}}</label>
|
||||||
|
</div>
|
||||||
|
<button type="submit" name="submit" value="submit" class="btn btn-default">{{_('Save')}}</button>
|
||||||
|
<a href="{{ url_for('admin.admin') }}" id="email_back" class="btn btn-default">{{_('Cancel')}}</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user