mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-11-04 01:03:02 +00:00 
			
		
		
		
	Added Scheduled Tasks Settings
This commit is contained in:
		
							
								
								
									
										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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -140,6 +140,11 @@ class _Settings(_Base):
 | 
				
			|||||||
    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 %}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user