Added toggle for task handler.

This commit is contained in:
arabcoders 2025-07-25 20:17:51 +03:00
parent dcaeb7acc8
commit 584d4e107e
6 changed files with 80 additions and 23 deletions

View file

@ -111,7 +111,7 @@
"youtu"
],
"css.styleSheets": [
"ui/assets/css/*.css"
"ui/app/assets/css/*.css"
],
"spellright.language": [
"en"

View file

@ -58,6 +58,7 @@ class Scheduler(metaclass=Singleton):
for job in self._jobs:
try:
self._jobs[job].stop()
LOG.debug(f"Stopped job '{job}'.")
except Exception as e:
LOG.exception(e)
LOG.error(f"Failed to stop job '{job}'. Error message '{e!s}'.")

View file

@ -35,6 +35,7 @@ class Task:
template: str = ""
cli: str = ""
auto_start: bool = True
enabled_handler: bool = True
def serialize(self) -> dict:
return self.__dict__
@ -100,7 +101,8 @@ class Tasks(metaclass=Singleton):
return Tasks._instance
async def on_shutdown(self, _: web.Application):
pass
self.clear(shutdown=True)
self._task_handler.on_shutdown(_)
def attach(self, _: web.Application):
"""
@ -116,6 +118,7 @@ class Tasks(metaclass=Singleton):
lambda data, _, **kwargs: self.save(**data.data), # noqa: ARG005
f"{__class__.__name__}.add",
)
self._task_handler.load()
def get_all(self) -> list[Task]:
"""Return the tasks."""
@ -155,7 +158,7 @@ class Tasks(metaclass=Singleton):
self._tasks.append(task)
if not task.timer or "[only_handler]" in task.name:
if not task.timer:
continue
try:
@ -361,12 +364,21 @@ class Tasks(metaclass=Singleton):
class HandleTask:
_tasks: Tasks
_handlers: list[type]
_scheduler: Scheduler
_config: Config
_task_name: str
def __init__(self, scheduler: Scheduler, tasks: Tasks, config: Config) -> None:
self._tasks = tasks
self._scheduler = scheduler
self._config = config
self._task_name: str = f"{__class__.__name__}._dispatcher"
def load(self) -> None:
self._handlers: list[type] = self._discover()
timer = config.tasks_handler_timer
timer: str = self._config.tasks_handler_timer
try:
from cronsim import CronSim
@ -375,18 +387,26 @@ class HandleTask:
timer = "15 */1 * * *"
LOG.error(f"Invalid timer format. '{e!s}'. Defaulting to '{timer}'.")
scheduler.add(
self._scheduler.add(
timer=timer,
func=self._dispatcher,
id=f"{__class__.__name__}._dispatcher",
)
def on_shutdown(self, _: web.Application) -> None:
"""
Handle shutdown event.
Args:
_: web.Application: The aiohttp application.
"""
if self._scheduler.has(self._task_name):
self._scheduler.remove(self._task_name)
def _dispatcher(self):
for task in self._tasks.get_all():
if "[no_handler]" in task.name:
continue
if not task.timer and "[only_handler]" not in task.name:
if not task.enabled_handler:
continue
try:

View file

@ -215,6 +215,28 @@
</div>
</div>
<div class="column is-6-tablet is-12-mobile">
<div class="field">
<label class="label is-inline" for="output_template">
<span class="icon"><i class="fa-solid fa-rss" /></span>
Enable Handler
</label>
<div class="control is-unselectable">
<input id="auto_start" type="checkbox" v-model="form.enabled_handler" :disabled="addInProgress"
class="switch is-success" />
<label for="auto_start" class="is-unselectable">
{{ form.enabled_handler ? 'Yes' : 'No' }}
</label>
</div>
<span class="help">
<span class="icon"><i class="fa-solid fa-info" /></span>
<span>Some URLs like YouTube channels and playlists can be monitored for new videos using RSS
feed independent of the timer. If enabled, the task will monitor the RSS feed for new videos and
automatically queue them for download if they are not already downloaded.</span>
</span>
</div>
</div>
<div class="column is-12">
<div class="field">
<label class="label is-inline" for="cli_options">
@ -260,22 +282,19 @@
</div>
<div class="column is-12">
<Message title="Tips" class="is-background-info-80 has-text-dark" icon="fas fa-info-circle">
<Message title="Tips" class="is-info is-background-info-80 has-text-dark" icon="fas fa-info-circle">
<span>
<ul>
<li>To enable YouTube RSS feed monitoring, The task URL must include a <code>channel_id</code> or
<code>playlist_id</code>. Other link types wont work.
</li>
<li>RSS monitoring runs every hour alongside the actual task execution. It checks each feed for new videos
and automatically queues any you havent downloaded yet.</li>
<li>To opt out of RSS monitoring for a specific task, append <code>[no_handler]</code> to that tasks name.
</li>
<li>To have the task only monitor RSS feed, do not set timer and add <code>[only_handler]</code> to that
tasks name.</li>
<li>RSS monitoring runs every hour alongside the actual task execution. Regardless if the task has timer set
or not. To opt out of RSS monitoring for a specific task, simply disable the <code>Enable Handler</code>
option. To have the task only monitor RSS feed, <b>do not set timer</b>.</li>
<li>RSS Feed monitoring will only work if you have <code>--download-archive</code> set in command options
for ytdlp.cli, preset or task.</li>
<li>If you don't have <code>--download-archive</code> set but <code>YTP_KEEP_ARCHIVE</code> environment
option is set to <code>true</code> which is the default, It will also work.
for ytdlp.cli, preset or task. If you don't have <code>--download-archive</code> set but
<code>YTP_KEEP_ARCHIVE</code> environment option is set to <code>true</code> which is the default, It will
also work.
</li>
</ul>
</span>
@ -320,9 +339,15 @@ onMounted(() => {
if (!props.task?.preset || '' === props.task.preset) {
form.preset = toRaw(config.app.default_preset)
}
if (typeof form.auto_start === 'undefined' || null === form.auto_start) {
form.auto_start = true
}
if (typeof form.enabled_handler === 'undefined' || null === form.enabled_handler) {
form.enabled_handler = true
}
})
const checkInfo = async (): Promise<void> => {

View file

@ -66,8 +66,8 @@ div.is-centered {
<div class="columns is-multiline" v-if="toggleForm">
<div class="column is-12">
<TaskForm :addInProgress="addInProgress" :reference="taskRef" :task="task" @cancel="resetForm(true);"
@submit="updateItem" />
<TaskForm :addInProgress="addInProgress" :reference="taskRef" :task="task as task_item"
@cancel="resetForm(true);" @submit="updateItem" />
</div>
</div>
@ -149,7 +149,7 @@ div.is-centered {
{{ remove_tags(item.name) }}
</NuxtLink>
</div>
<div>
<div class="is-unselectable">
<span class="icon-text">
<span class="icon">
<i class="fa-solid"
@ -160,6 +160,11 @@ div.is-centered {
</span>
</span>
&nbsp;
<span class="icon-text">
<span class="icon"><i class="fa-solid fa-rss" /></span>
<span>{{ item.enabled_handler ? 'Enabled' : 'Disabled' }}</span>
</span>
&nbsp;
<span class="icon-text" v-if="item.preset">
<span class="icon"><i class="fa-solid fa-tv" /></span>
<span>{{ item.preset ?? config.app.default_preset }}</span>
@ -233,6 +238,11 @@ div.is-centered {
:class="{ 'fa-circle-pause': item.auto_start, 'fa-circle-play': !item.auto_start }" />
</span>
</div>
<div class="control">
<span class="icon" v-tooltip="`RSS monitoring is ${item.enabled_handler ? 'enabled' : 'disabled'}`">
<i class="fa-solid fa-rss has-text-success" />
</span>
</div>
<div class="control">
<a class="has-text-primary" v-tooltip="'Export task.'" @click.prevent="exportItem(item)">
<span class="icon"><i class="fa-solid fa-file-export" /></span>
@ -544,7 +554,7 @@ const deleteItem = async (item: task_item) => {
toast.success('Task deleted.')
}
const updateItem = async ({ reference, task }: { reference?: string, task: task_item }) => {
const updateItem = async ({ reference, task }: { reference?: string | null | undefined, task: task_item }) => {
if (reference) {
// -- find the task index.
const index = tasks.value.findIndex((t) => t?.id === reference)

View file

@ -9,6 +9,7 @@ type task_item = {
timer?: string,
in_progress?: boolean,
auto_start?: boolean,
enabled_handler?: boolean,
}
type exported_task = task_item & { _type: string, _version: string }