Skip to content

Commit 82eeafd

Browse files
authored
Merge pull request #538 from arabcoders/dev
Migrated tasks to use the new events system for queue.
2 parents 20142e4 + 381d092 commit 82eeafd

File tree

11 files changed

+249
-149
lines changed

11 files changed

+249
-149
lines changed

FAQ.md

+26-14
Original file line numberDiff line numberDiff line change
@@ -913,25 +913,37 @@ is an example of how to do it for debian based systems.
913913
```yaml
914914
services:
915915
watchstate:
916-
image: ghcr.io/arabcoders/watchstate:latest
917-
# To change the user/group id associated with the tool change the following line.
918-
user: "${UID:-1000}:${GID:-1000}"
919-
group_add:
920-
- "44" # Add video group to the container.
921-
- "110" # Add render group to the container.
922916
container_name: watchstate
917+
image: ghcr.io/arabcoders/watchstate:latest # The image to use. you can use the latest or dev tag.
918+
user: "${UID:-1000}:${GID:-1000}" # user and group id to run the container under.
919+
group_add:
920+
- "44" # Add video group to the container.
921+
- "105" # Add render group to the container.
923922
restart: unless-stopped
924923
ports:
925-
- "8080:8080" # The port which will serve WebUI + API + Webhooks
924+
- "8080:8080" # The port which will serve WebUI + API + Webhooks
925+
devices:
926+
- /dev/dri:/dev/dri # mount the dri devices to the container.
926927
volumes:
927-
- ./data:/config:rw # mount current directory to container /config directory.
928-
- /dev/dri:/dev/dri # mount the dri devices to the container.
929-
- /storage/media:/media:ro # mount your media directory to the container.
928+
- ./data:/config:rw # mount current directory to container /config directory.
929+
- /storage/media:/media:ro # mount your media directory to the container.
930930
```
931931
932-
This setup should work for VAAPI encoding in `x86_64` containers, for other architectures you need to adjust the
933-
`/dev/dri` to match your hardware. There are currently an issue with nvidia h264_nvenc encoding, the alpine build for
934-
`ffmpeg`doesn't include the codec.
932+
This setup should work for VAAPI encoding in `x86_64` containers, There are currently an issue with nvidia h264_nvenc
933+
encoding, the alpine build for`ffmpeg` doesn't include the codec. i am looking for a way include the codec without
934+
ballooning the image size by 600MB+. If you have a solution please let me know.
935935

936-
Note: the tip about adding the group_add came from the user `binarypancakes` in discord.
936+
Please know that your `video`, `render` group id might be different then mine, you can run the follow command in docker
937+
host server to get the group ids for both groups.
938+
939+
```bash
940+
$ cat /etc/group | grep -E 'render|video'
937941
942+
video:x:44:your_docker_username
943+
render:x:105:your_docker_username
944+
```
945+
946+
In my docker host the group id for `video` is `44` and for `render` is `105`. change what needed in the `compose.yaml`
947+
file to match your setup.
948+
949+
Note: the tip about adding the group_add came from the user `binarypancakes` in discord.

config/config.php

+1
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@
299299
'enabled' => true,
300300
'timer' => '* * * * *',
301301
'args' => '-v',
302+
'hide' => true,
302303
],
303304
],
304305
];

container/files/init-container.sh

+3
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ fi
8888
echo "[$(date +"%Y-%m-%dT%H:%M:%S%z")] Caching tool routes."
8989
/opt/bin/console system:routes
9090

91+
echo "[$(date +"%Y-%m-%dT%H:%M:%S%z")] Caching events listeners."
92+
/opt/bin/console events:cache
93+
9194
echo "[$(date +"%Y-%m-%dT%H:%M:%S%z")] Running database migrations."
9295
/opt/bin/console system:db:migrations
9396

frontend/pages/events/index.vue

+6-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,12 @@
8585
</time>
8686
</span>
8787
<span class="card-footer-item">
88-
<span v-if="!item.updated_at" class="icon"><i class="fas fa-spinner fa-spin"></i></span>
88+
<template v-if="!item.updated_at">
89+
<span v-if="0 === item.status" class="icon">
90+
<i class="fas fa-spinner fa-spin"></i>
91+
</span>
92+
<span v-else>None</span>
93+
</template>
8994
<template v-else>
9095
<span class="icon"><i class="fas fa-calendar-alt"></i></span>
9196
<time class="has-tooltip" v-tooltip="`Updated at: ${moment(item.updated_at)}`">

frontend/pages/tasks.vue

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@
122122
<span class="icon"><i class="fas fa-clock" :class="{ 'fa-spin': task.queued }"></i></span>
123123
<span>
124124
<template v-if="!task.queued">Queue Task</template>
125-
<template v-else>Remove from queue</template>
125+
<template v-else>Cancel Task</template>
126126
</span>
127127
</span>
128128
</button>
@@ -239,7 +239,7 @@ const queueTask = async task => {
239239
try {
240240
const response = await request(`/tasks/${task.name}/queue`, {method: is_queued ? 'DELETE' : 'POST'})
241241
if (response.ok) {
242-
notification('success', 'Success', `Task '${task.name}' has been ${is_queued ? 'removed from the queue' : 'queued'}.`)
242+
notification('success', 'Success', `Task '${task.name}' has been ${is_queued ? 'cancelled' : 'queued'}.`)
243243
task.queued = !is_queued
244244
if (task.queued) {
245245
queued.value.push(task.name)

src/API/Tasks/Index.php renamed to src/API/Tasks.php

+55-29
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,26 @@
22

33
declare(strict_types=1);
44

5-
namespace App\API\Tasks;
5+
namespace App\API;
66

77
use App\Commands\System\TasksCommand;
88
use App\Libs\Attributes\Route\Get;
99
use App\Libs\Attributes\Route\Route;
1010
use App\Libs\Enums\Http\Status;
11+
use App\Model\Events\Event;
12+
use App\Model\Events\EventsRepository;
13+
use App\Model\Events\EventsTable;
14+
use App\Model\Events\EventStatus;
1115
use Cron\CronExpression;
12-
use DateInterval;
1316
use Psr\Http\Message\ResponseInterface as iResponse;
1417
use Psr\Http\Message\ServerRequestInterface as iRequest;
15-
use Psr\SimpleCache\CacheInterface as iCache;
1618
use Psr\SimpleCache\InvalidArgumentException;
1719

18-
final class Index
20+
final class Tasks
1921
{
2022
public const string URL = '%{api.prefix}/tasks';
2123

22-
public function __construct(private readonly iCache $cache)
24+
public function __construct(private EventsRepository $eventsRepo)
2325
{
2426
}
2527

@@ -29,22 +31,28 @@ public function __construct(private readonly iCache $cache)
2931
#[Get(self::URL . '[/]', name: 'tasks.index')]
3032
public function tasksIndex(): iResponse
3133
{
32-
$queuedTasks = $this->cache->get('queued_tasks', []);
33-
$response = [
34-
'tasks' => [],
35-
'queued' => $queuedTasks,
36-
'status' => isTaskWorkerRunning(),
37-
];
34+
$tasks = [];
3835

3936
foreach (TasksCommand::getTasks() as $task) {
4037
$task = self::formatTask($task);
41-
$task['queued'] = in_array(ag($task, 'name'), $queuedTasks);
38+
if (true === (bool)ag($task, 'hide', false)) {
39+
continue;
40+
}
4241

42+
$task['queued'] = null !== $this->isQueued(ag($task, 'name'));
43+
$tasks[] = $task;
44+
}
4345

44-
$response['tasks'][] = $task;
46+
$queued = [];
47+
foreach (array_filter($tasks, fn($item) => $item['queued'] === true) as $item) {
48+
$queued[] = $item['name'];
4549
}
4650

47-
return api_response(Status::OK, $response);
51+
return api_response(Status::OK, [
52+
'tasks' => $tasks,
53+
'queued' => $queued,
54+
'status' => isTaskWorkerRunning(),
55+
]);
4856
}
4957

5058
/**
@@ -59,29 +67,41 @@ public function taskQueue(iRequest $request, string $id): iResponse
5967
return api_error('Task not found.', Status::NOT_FOUND);
6068
}
6169

62-
$queuedTasks = $this->cache->get('queued_tasks', []);
70+
$queuedTask = $this->isQueued(ag($task, 'name'));
6371

6472
if ('POST' === $request->getMethod()) {
65-
$queuedTasks[] = $id;
66-
$this->cache->set('queued_tasks', $queuedTasks, new DateInterval('P3D'));
67-
return api_response(Status::ACCEPTED, ['queue' => $queuedTasks]);
73+
if (null !== $queuedTask) {
74+
return api_error('Task already queued.', Status::CONFLICT);
75+
}
76+
77+
$event = queueEvent(TasksCommand::NAME, ['name' => $id], [
78+
EventsTable::COLUMN_REFERENCE => r('task://{name}', ['name' => $id]),
79+
]);
80+
81+
return api_response(Status::ACCEPTED, $event->getAll());
6882
}
6983

7084
if ('DELETE' === $request->getMethod()) {
71-
$queuedTasks = array_filter($queuedTasks, fn($v) => $v !== $id);
72-
$this->cache->set('queued_tasks', $queuedTasks, new DateInterval('P3D'));
73-
return api_response(Status::OK, ['queue' => $queuedTasks]);
85+
if (null === $queuedTask) {
86+
return api_error('Task not queued.', Status::NOT_FOUND);
87+
}
88+
89+
if ($queuedTask->status === EventStatus::RUNNING) {
90+
return api_error('Cannot remove task in running state.', Status::BAD_REQUEST);
91+
}
92+
93+
$queuedTask->status = EventStatus::CANCELLED;
94+
$this->eventsRepo->save($queuedTask);
95+
96+
return api_response(Status::OK);
7497
}
7598

7699
return api_response(Status::OK, [
77100
'task' => $id,
78-
'is_queued' => in_array($id, $queuedTasks),
101+
'is_queued' => null !== $queuedTask,
79102
]);
80103
}
81104

82-
/**
83-
* @throws InvalidArgumentException
84-
*/
85105
#[Get(self::URL . '/{id:[a-zA-Z0-9_-]+}[/]', name: 'tasks.task.view')]
86106
public function taskView(string $id): iResponse
87107
{
@@ -91,10 +111,8 @@ public function taskView(string $id): iResponse
91111
return api_error('Task not found.', Status::NOT_FOUND);
92112
}
93113

94-
$queuedTasks = $this->cache->get('queued_tasks', []);
95-
96-
$data = Index::formatTask($task);
97-
$data['queued'] = in_array(ag($task, 'name'), $queuedTasks);
114+
$data = Tasks::formatTask($task);
115+
$data['queued'] = null !== $this->isQueued(ag($task, 'name'));
98116

99117
return api_response(Status::OK, $data);
100118
}
@@ -115,6 +133,7 @@ private function formatTask(array $task): array
115133
'prev_run' => null,
116134
'command' => ag($task, 'command'),
117135
'args' => ag($task, 'args'),
136+
'hide' => (bool)ag($task, 'hide', false),
118137
];
119138

120139
if (!is_string($item['command'])) {
@@ -136,4 +155,11 @@ private function formatTask(array $task): array
136155

137156
return $item;
138157
}
158+
159+
private function isQueued(string $id): Event|null
160+
{
161+
return $this->eventsRepo->findByReference(r('task://{name}', ['name' => $id]), [
162+
EventsTable::COLUMN_STATUS => EventStatus::PENDING->value
163+
]);
164+
}
139165
}

src/Commands/Events/DispatchCommand.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ private function runEvent(Event $event): void
142142
$event->updated_at = (string)makeDate();
143143
$this->repo->save($event);
144144

145-
$this->logger->error($errorLog);
145+
$this->logger->error($errorLog, ['trace' => $e->getTrace()]);
146146
}
147147
}
148148
}

src/Commands/System/IndexCommand.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
#[Cli(command: self::ROUTE)]
2121
final class IndexCommand extends Command
2222
{
23-
public const ROUTE = 'system:index';
23+
public const string ROUTE = 'system:index';
2424

25-
public const TASK_NAME = 'indexes';
25+
public const string TASK_NAME = 'indexes';
2626

2727
/**
2828
* Class constructor.

0 commit comments

Comments
 (0)