parent
9bdf98f699
commit
c8cab9d2fd
@ -0,0 +1,221 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
|
||||
namespace OC\Core\Controller; |
||||
|
||||
use OC\Files\AppData\AppData; |
||||
use OCA\Core\ResponseDefinitions; |
||||
use OCP\AppFramework\Http; |
||||
use OCP\AppFramework\Http\Attribute\AnonRateLimit; |
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired; |
||||
use OCP\AppFramework\Http\Attribute\PublicPage; |
||||
use OCP\AppFramework\Http\Attribute\UserRateLimit; |
||||
use OCP\AppFramework\Http\DataResponse; |
||||
use OCP\AppFramework\Http\FileDisplayResponse; |
||||
use OCP\IL10N; |
||||
use OCP\IRequest; |
||||
use OCP\TextToImage\Exception\TaskNotFoundException; |
||||
use OCP\TextToImage\Task; |
||||
use OCP\TextToImage\IManager; |
||||
use OCP\PreConditionNotMetException; |
||||
|
||||
/** |
||||
* @psalm-import-type CoreTextToImageTask from ResponseDefinitions |
||||
*/ |
||||
class TextToImageApiController extends \OCP\AppFramework\OCSController { |
||||
public function __construct( |
||||
string $appName, |
||||
IRequest $request, |
||||
private IManager $textToImageManager, |
||||
private IL10N $l, |
||||
private ?string $userId, |
||||
private AppData $appData, |
||||
) { |
||||
parent::__construct($appName, $request); |
||||
} |
||||
|
||||
/** |
||||
* @PublicPage |
||||
* |
||||
* Check whether this feature is available |
||||
* |
||||
* @return DataResponse<Http::STATUS_OK, array{isAvailable: bool}, array{}> |
||||
*/ |
||||
public function isAvailable(): DataResponse { |
||||
return new DataResponse([ |
||||
'isAvailable' => $this->textToImageManager->hasProviders(), |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* This endpoint allows scheduling a text to image task |
||||
* |
||||
* @param string $input Input text |
||||
* @param string $appId ID of the app that will execute the task |
||||
* @param string $identifier An arbitrary identifier for the task |
||||
* |
||||
* @return DataResponse<Http::STATUS_OK, array{task: CoreTextToImageTask}, array{}>|DataResponse<Http::STATUS_BAD_REQUEST|Http::STATUS_PRECONDITION_FAILED, array{message: string}, array{}> |
||||
* |
||||
* 200: Task scheduled successfully |
||||
* 400: Scheduling task is not possible |
||||
* 412: Scheduling task is not possible |
||||
*/ |
||||
#[PublicPage] |
||||
#[UserRateLimit(limit: 20, period: 120)] |
||||
#[AnonRateLimit(limit: 5, period: 120)] |
||||
public function schedule(string $input, string $type, string $appId, string $identifier = ''): DataResponse { |
||||
$task = new Task($input, $appId, $this->userId, $identifier); |
||||
try { |
||||
$this->textToImageManager->scheduleTask($task); |
||||
|
||||
$json = $task->jsonSerialize(); |
||||
|
||||
return new DataResponse([ |
||||
'task' => $json, |
||||
]); |
||||
} catch (PreConditionNotMetException) { |
||||
return new DataResponse(['message' => $this->l->t('No text to image provider is available')], Http::STATUS_PRECONDITION_FAILED); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This endpoint allows checking the status and results of a task. |
||||
* Tasks are removed 1 week after receiving their last update. |
||||
* |
||||
* @param int $id The id of the task |
||||
* |
||||
* @return DataResponse<Http::STATUS_OK, array{task: CoreTextToImageTask}, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}> |
||||
* |
||||
* 200: Task returned |
||||
* 404: Task not found |
||||
*/ |
||||
#[PublicPage] |
||||
public function getTask(int $id): DataResponse { |
||||
try { |
||||
$task = $this->textToImageManager->getUserTask($id, $this->userId); |
||||
|
||||
$json = $task->jsonSerialize(); |
||||
|
||||
return new DataResponse([ |
||||
'task' => $json, |
||||
]); |
||||
} catch (TaskNotFoundException $e) { |
||||
return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); |
||||
} catch (\RuntimeException $e) { |
||||
return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This endpoint allows checking the status and results of a task. |
||||
* Tasks are removed 1 week after receiving their last update. |
||||
* |
||||
* @param int $id The id of the task |
||||
* |
||||
* @return FileDisplayResponse<Http::STATUS_OK, array{'Content-Type': string}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}> |
||||
* |
||||
* 200: Task returned |
||||
* 404: Task not found |
||||
*/ |
||||
#[PublicPage] |
||||
public function getImage(int $id): DataResponse|FileDisplayResponse { |
||||
try { |
||||
$task = $this->textToImageManager->getUserTask($id, $this->userId); |
||||
try { |
||||
$folder = $this->appData->getFolder('text2image'); |
||||
} catch(\OCP\Files\NotFoundException) { |
||||
$folder = $this->appData->newFolder('text2image'); |
||||
} |
||||
$file = $folder->getFile((string)$task->getId()); |
||||
$info = getimagesizefromstring($file->getContent()); |
||||
|
||||
return new FileDisplayResponse($file, Http::STATUS_OK, ['Content-Type' => image_type_to_mime_type($info[2])]); |
||||
} catch (TaskNotFoundException) { |
||||
return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); |
||||
} catch (\RuntimeException) { |
||||
return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); |
||||
} catch (\OCP\Files\NotFoundException) { |
||||
return new DataResponse(['message' => $this->l->t('Image not found')], Http::STATUS_NOT_FOUND); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This endpoint allows to delete a scheduled task for a user |
||||
* |
||||
* @param int $id The id of the task |
||||
* |
||||
* @return DataResponse<Http::STATUS_OK, array{task: CoreTextToImageTask}, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}> |
||||
* |
||||
* 200: Task returned |
||||
* 404: Task not found |
||||
*/ |
||||
#[NoAdminRequired] |
||||
public function deleteTask(int $id): DataResponse { |
||||
try { |
||||
$task = $this->textToImageManager->getUserTask($id, $this->userId); |
||||
|
||||
$this->textToImageManager->deleteTask($task); |
||||
|
||||
$json = $task->jsonSerialize(); |
||||
|
||||
return new DataResponse([ |
||||
'task' => $json, |
||||
]); |
||||
} catch (TaskNotFoundException $e) { |
||||
return new DataResponse(['message' => $this->l->t('Task not found')], Http::STATUS_NOT_FOUND); |
||||
} catch (\RuntimeException $e) { |
||||
return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); |
||||
} |
||||
} |
||||
|
||||
|
||||
/** |
||||
* This endpoint returns a list of tasks of a user that are related |
||||
* with a specific appId and optionally with an identifier |
||||
* |
||||
* @param string $appId ID of the app |
||||
* @param string|null $identifier An arbitrary identifier for the task |
||||
* @return DataResponse<Http::STATUS_OK, array{tasks: CoreTextToImageTask[]}, array{}>|DataResponse<Http::STATUS_INTERNAL_SERVER_ERROR, array{message: string}, array{}> |
||||
* |
||||
* 200: Task list returned |
||||
*/ |
||||
#[NoAdminRequired] |
||||
public function listTasksByApp(string $appId, ?string $identifier = null): DataResponse { |
||||
try { |
||||
$tasks = $this->textToImageManager->getUserTasksByApp($this->userId, $appId, $identifier); |
||||
/** @var CoreTextToImageTask[] $json */ |
||||
$json = array_map(static function (Task $task) { |
||||
return $task->jsonSerialize(); |
||||
}, $tasks); |
||||
|
||||
return new DataResponse([ |
||||
'tasks' => $json, |
||||
]); |
||||
} catch (\RuntimeException $e) { |
||||
return new DataResponse(['message' => $this->l->t('Internal error')], Http::STATUS_INTERNAL_SERVER_ERROR); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,100 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OC\Core\Migrations; |
||||
|
||||
use Closure; |
||||
use OCP\DB\ISchemaWrapper; |
||||
use OCP\DB\Types; |
||||
use OCP\Migration\IOutput; |
||||
use OCP\Migration\SimpleMigrationStep; |
||||
|
||||
/** |
||||
* Introduce text2image_tasks table |
||||
*/ |
||||
class Version28000Date20230906104802 extends SimpleMigrationStep { |
||||
/** |
||||
* @param IOutput $output |
||||
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` |
||||
* @param array $options |
||||
* @return null|ISchemaWrapper |
||||
*/ |
||||
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { |
||||
/** @var ISchemaWrapper $schema */ |
||||
$schema = $schemaClosure(); |
||||
$changed = false; |
||||
if (!$schema->hasTable('text2image_tasks')) { |
||||
$table = $schema->createTable('text2image_tasks'); |
||||
|
||||
$table->addColumn('id', Types::BIGINT, [ |
||||
'notnull' => true, |
||||
'length' => 64, |
||||
'autoincrement' => true, |
||||
]); |
||||
$table->addColumn('input', Types::TEXT, [ |
||||
'notnull' => true, |
||||
]); |
||||
$table->addColumn('status', Types::INTEGER, [ |
||||
'notnull' => false, |
||||
'length' => 6, |
||||
'default' => 0, |
||||
]); |
||||
$table->addColumn('user_id', Types::STRING, [ |
||||
'notnull' => false, |
||||
'length' => 64, |
||||
]); |
||||
$table->addColumn('app_id', Types::STRING, [ |
||||
'notnull' => true, |
||||
'length' => 32, |
||||
'default' => '', |
||||
]); |
||||
$table->addColumn('identifier', Types::STRING, [ |
||||
'notnull' => true, |
||||
'length' => 255, |
||||
'default' => '', |
||||
]); |
||||
$table->addColumn('last_updated', Types::INTEGER, [ |
||||
'notnull' => false, |
||||
'length' => 4, |
||||
'default' => 0, |
||||
'unsigned' => true, |
||||
]); |
||||
|
||||
$table->setPrimaryKey(['id'], 't2i_tasks_id_index'); |
||||
$table->addIndex(['last_updated'], 't2i_tasks_updated'); |
||||
$table->addIndex(['status'], 't2i_tasks_status'); |
||||
$table->addIndex(['user_id', 'app_id', 'identifier'], 't2i_tasks_uid_appid_ident'); |
||||
|
||||
$changed = true; |
||||
} |
||||
|
||||
if ($changed) { |
||||
return $schema; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
} |
||||
@ -0,0 +1,119 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
namespace OC\TextToImage\Db; |
||||
|
||||
use OCP\AppFramework\Db\Entity; |
||||
use OCP\Files\AppData\IAppDataFactory; |
||||
use OCP\Files\NotFoundException; |
||||
use OCP\Files\NotPermittedException; |
||||
use OCP\Image; |
||||
use OCP\TextToImage\Task as OCPTask; |
||||
|
||||
/** |
||||
* @method setLastUpdated(int $lastUpdated) |
||||
* @method int getLastUpdated() |
||||
* @method setInput(string $type) |
||||
* @method string getInput() |
||||
* @method setResultPath(string $resultPath) |
||||
* @method string getResultPath() |
||||
* @method setStatus(int $type) |
||||
* @method int getStatus() |
||||
* @method setUserId(?string $userId) |
||||
* @method string|null getUserId() |
||||
* @method setAppId(string $type) |
||||
* @method string getAppId() |
||||
* @method setIdentifier(string $identifier) |
||||
* @method string getIdentifier() |
||||
*/ |
||||
class Task extends Entity { |
||||
protected $lastUpdated; |
||||
protected $type; |
||||
protected $input; |
||||
protected $status; |
||||
protected $userId; |
||||
protected $appId; |
||||
protected $identifier; |
||||
|
||||
/** |
||||
* @var string[] |
||||
*/ |
||||
public static array $columns = ['id', 'last_updated', 'input', 'status', 'user_id', 'app_id', 'identifier']; |
||||
|
||||
/** |
||||
* @var string[] |
||||
*/ |
||||
public static array $fields = ['id', 'lastUpdated', 'input', 'status', 'userId', 'appId', 'identifier']; |
||||
|
||||
|
||||
public function __construct() { |
||||
// add types in constructor |
||||
$this->addType('id', 'integer'); |
||||
$this->addType('lastUpdated', 'integer'); |
||||
$this->addType('input', 'string'); |
||||
$this->addType('status', 'integer'); |
||||
$this->addType('userId', 'string'); |
||||
$this->addType('appId', 'string'); |
||||
$this->addType('identifier', 'string'); |
||||
} |
||||
|
||||
public function toRow(): array { |
||||
return array_combine(self::$columns, array_map(function ($field) { |
||||
return $this->{'get'.ucfirst($field)}(); |
||||
}, self::$fields)); |
||||
} |
||||
|
||||
public static function fromPublicTask(OCPTask $task): Task { |
||||
/** @var Task $dbTask */ |
||||
$dbTask = Task::fromParams([ |
||||
'id' => $task->getId(), |
||||
'lastUpdated' => time(), |
||||
'status' => $task->getStatus(), |
||||
'input' => $task->getInput(), |
||||
'userId' => $task->getUserId(), |
||||
'appId' => $task->getAppId(), |
||||
'identifier' => $task->getIdentifier(), |
||||
]); |
||||
return $dbTask; |
||||
} |
||||
|
||||
public function toPublicTask(): OCPTask { |
||||
$task = new OCPTask($this->getInput(), $this->getAppId(), $this->getuserId(), $this->getIdentifier()); |
||||
$task->setId($this->getId()); |
||||
$task->setStatus($this->getStatus()); |
||||
$appData = \OC::$server->get(IAppDataFactory::class)->get('core'); |
||||
try { |
||||
try { |
||||
$folder = $appData->getFolder('text2image'); |
||||
} catch(NotFoundException) { |
||||
$folder = $appData->newFolder('text2image'); |
||||
} |
||||
$task->setOutputImage(new Image(base64_encode($folder->getFile((string)$task->getId())->getContent()))); |
||||
} catch (NotFoundException|NotPermittedException) { |
||||
// noop |
||||
} |
||||
return $task; |
||||
} |
||||
} |
||||
@ -0,0 +1,118 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
namespace OC\TextToImage\Db; |
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException; |
||||
use OCP\AppFramework\Db\Entity; |
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException; |
||||
use OCP\AppFramework\Db\QBMapper; |
||||
use OCP\AppFramework\Utility\ITimeFactory; |
||||
use OCP\DB\Exception; |
||||
use OCP\IDBConnection; |
||||
|
||||
/** |
||||
* @extends QBMapper<Task> |
||||
*/ |
||||
class TaskMapper extends QBMapper { |
||||
public function __construct( |
||||
IDBConnection $db, |
||||
private ITimeFactory $timeFactory, |
||||
) { |
||||
parent::__construct($db, 'text2image_tasks', Task::class); |
||||
} |
||||
|
||||
/** |
||||
* @param int $id |
||||
* @return Task |
||||
* @throws Exception |
||||
* @throws DoesNotExistException |
||||
* @throws MultipleObjectsReturnedException |
||||
*/ |
||||
public function find(int $id): Task { |
||||
$qb = $this->db->getQueryBuilder(); |
||||
$qb->select(Task::$columns) |
||||
->from($this->tableName) |
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id))); |
||||
return $this->findEntity($qb); |
||||
} |
||||
|
||||
/** |
||||
* @param int $id |
||||
* @param string|null $userId |
||||
* @return Task |
||||
* @throws DoesNotExistException |
||||
* @throws Exception |
||||
* @throws MultipleObjectsReturnedException |
||||
*/ |
||||
public function findByIdAndUser(int $id, ?string $userId): Task { |
||||
$qb = $this->db->getQueryBuilder(); |
||||
$qb->select(Task::$columns) |
||||
->from($this->tableName) |
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id))); |
||||
if ($userId === null) { |
||||
$qb->andWhere($qb->expr()->isNull('user_id')); |
||||
} else { |
||||
$qb->andWhere($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))); |
||||
} |
||||
return $this->findEntity($qb); |
||||
} |
||||
|
||||
/** |
||||
* @param string $userId |
||||
* @param string $appId |
||||
* @param string|null $identifier |
||||
* @return array |
||||
* @throws Exception |
||||
*/ |
||||
public function findUserTasksByApp(string $userId, string $appId, ?string $identifier = null): array { |
||||
$qb = $this->db->getQueryBuilder(); |
||||
$qb->select(Task::$columns) |
||||
->from($this->tableName) |
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))) |
||||
->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId))); |
||||
if ($identifier !== null) { |
||||
$qb->andWhere($qb->expr()->eq('identifier', $qb->createPositionalParameter($identifier))); |
||||
} |
||||
return $this->findEntities($qb); |
||||
} |
||||
|
||||
/** |
||||
* @param int $timeout |
||||
* @return int the number of deleted tasks |
||||
* @throws Exception |
||||
*/ |
||||
public function deleteOlderThan(int $timeout): int { |
||||
$qb = $this->db->getQueryBuilder(); |
||||
$qb->delete($this->tableName) |
||||
->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter(time() - $timeout))); |
||||
return $qb->executeStatement(); |
||||
} |
||||
|
||||
public function update(Entity $entity): Entity { |
||||
$entity->setLastUpdated($this->timeFactory->now()->getTimestamp()); |
||||
return parent::update($entity); |
||||
} |
||||
} |
||||
@ -0,0 +1,245 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
namespace OC\TextToImage; |
||||
|
||||
use OC\AppFramework\Bootstrap\Coordinator; |
||||
use OC\TextToImage\Db\Task as DbTask; |
||||
use OCP\Files\AppData\IAppDataFactory; |
||||
use OCP\Files\IAppData; |
||||
use OCP\IConfig; |
||||
use OCP\TextToImage\Exception\TaskNotFoundException; |
||||
use OCP\TextToImage\IManager; |
||||
use OCP\TextToImage\Task; |
||||
use OC\TextToImage\Db\TaskMapper; |
||||
use OCP\AppFramework\Db\DoesNotExistException; |
||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException; |
||||
use OCP\BackgroundJob\IJobList; |
||||
use OCP\DB\Exception; |
||||
use OCP\IServerContainer; |
||||
use OCP\TextToImage\IProvider; |
||||
use OCP\PreConditionNotMetException; |
||||
use Psr\Log\LoggerInterface; |
||||
use RuntimeException; |
||||
use Throwable; |
||||
|
||||
class Manager implements IManager { |
||||
/** @var ?IProvider[] */ |
||||
private ?array $providers = null; |
||||
private IAppData $appData; |
||||
|
||||
public function __construct( |
||||
private IServerContainer $serverContainer, |
||||
private Coordinator $coordinator, |
||||
private LoggerInterface $logger, |
||||
private IJobList $jobList, |
||||
private TaskMapper $taskMapper, |
||||
private IConfig $config, |
||||
private IAppDataFactory $appDataFactory, |
||||
) { |
||||
$this->appData = $this->appDataFactory->get('core'); |
||||
} |
||||
|
||||
public function getProviders(): array { |
||||
$context = $this->coordinator->getRegistrationContext(); |
||||
if ($context === null) { |
||||
return []; |
||||
} |
||||
|
||||
if ($this->providers !== null) { |
||||
return $this->providers; |
||||
} |
||||
|
||||
$this->providers = []; |
||||
|
||||
foreach ($context->getTextToImageProviders() as $providerServiceRegistration) { |
||||
$class = $providerServiceRegistration->getService(); |
||||
try { |
||||
$this->providers[$class] = $this->serverContainer->get($class); |
||||
} catch (Throwable $e) { |
||||
$this->logger->error('Failed to load Text to image provider ' . $class, [ |
||||
'exception' => $e, |
||||
]); |
||||
} |
||||
} |
||||
|
||||
return $this->providers; |
||||
} |
||||
|
||||
public function hasProviders(): bool { |
||||
$context = $this->coordinator->getRegistrationContext(); |
||||
if ($context === null) { |
||||
return false; |
||||
} |
||||
return count($context->getTextToImageProviders()) > 0; |
||||
} |
||||
|
||||
/** |
||||
* @inheritDoc |
||||
*/ |
||||
public function runTask(Task $task): void { |
||||
if (!$this->hasProviders()) { |
||||
throw new PreConditionNotMetException('No text to image provider is installed that can handle this task'); |
||||
} |
||||
$providers = $this->getProviders(); |
||||
|
||||
$json = $this->config->getAppValue('core', 'ai.text2image_provider', ''); |
||||
if ($json !== '') { |
||||
$className = json_decode($json, true); |
||||
$provider = current(array_filter($providers, fn ($provider) => $provider::class === $className)); |
||||
if ($provider !== false) { |
||||
$providers = [$provider]; |
||||
} |
||||
} |
||||
|
||||
foreach ($providers as $provider) { |
||||
try { |
||||
$task->setStatus(Task::STATUS_RUNNING); |
||||
if ($task->getId() === null) { |
||||
$taskEntity = $this->taskMapper->insert(DbTask::fromPublicTask($task)); |
||||
$task->setId($taskEntity->getId()); |
||||
} else { |
||||
$this->taskMapper->update(DbTask::fromPublicTask($task)); |
||||
} |
||||
try { |
||||
$folder = $this->appData->getFolder('text2image'); |
||||
} catch(\OCP\Files\NotFoundException $e) { |
||||
$folder = $this->appData->newFolder('text2image'); |
||||
} |
||||
$file = $folder->newFile((string) $task->getId()); |
||||
$provider->generate($task->getInput(), $file->write()); |
||||
$task->setResultPath($file->getName()); |
||||
$task->setStatus(Task::STATUS_SUCCESSFUL); |
||||
$this->taskMapper->update(DbTask::fromPublicTask($task)); |
||||
return; |
||||
} catch (\RuntimeException $e) { |
||||
$this->logger->info('Text2Image generation using provider ' . $provider->getName() . ' failed', ['exception' => $e]); |
||||
$task->setStatus(Task::STATUS_FAILED); |
||||
$this->taskMapper->update(DbTask::fromPublicTask($task)); |
||||
throw $e; |
||||
} catch (\Throwable $e) { |
||||
$this->logger->info('Text2Image generation using provider ' . $provider->getName() . ' failed', ['exception' => $e]); |
||||
$task->setStatus(Task::STATUS_FAILED); |
||||
$this->taskMapper->update(DbTask::fromPublicTask($task)); |
||||
throw new RuntimeException('Text2Image generation using provider ' . $provider->getName() . ' failed: ' . $e->getMessage(), 0, $e); |
||||
} |
||||
} |
||||
|
||||
throw new RuntimeException('Could not run task'); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDoc |
||||
* @throws Exception |
||||
*/ |
||||
public function scheduleTask(Task $task): void { |
||||
if (!$this->hasProviders()) { |
||||
throw new PreConditionNotMetException('No text to image provider is installed that can handle this task'); |
||||
} |
||||
$task->setStatus(Task::STATUS_SCHEDULED); |
||||
$taskEntity = DbTask::fromPublicTask($task); |
||||
$this->taskMapper->insert($taskEntity); |
||||
$task->setId($taskEntity->getId()); |
||||
$this->jobList->add(TaskBackgroundJob::class, [ |
||||
'taskId' => $task->getId() |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritDoc |
||||
*/ |
||||
public function deleteTask(Task $task): void { |
||||
$taskEntity = DbTask::fromPublicTask($task); |
||||
$this->taskMapper->delete($taskEntity); |
||||
$this->jobList->remove(TaskBackgroundJob::class, [ |
||||
'taskId' => $task->getId() |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* Get a task from its id |
||||
* |
||||
* @param int $id The id of the task |
||||
* @return Task |
||||
* @throws RuntimeException If the query failed |
||||
* @throws NotFoundException If the task could not be found |
||||
*/ |
||||
public function getTask(int $id): Task { |
||||
try { |
||||
$taskEntity = $this->taskMapper->find($id); |
||||
return $taskEntity->toPublicTask(); |
||||
} catch (DoesNotExistException $e) { |
||||
throw new TaskNotFoundException('Could not find task with the provided id'); |
||||
} catch (MultipleObjectsReturnedException $e) { |
||||
throw new RuntimeException('Could not uniquely identify task with given id', 0, $e); |
||||
} catch (Exception $e) { |
||||
throw new RuntimeException('Failure while trying to find task by id: ' . $e->getMessage(), 0, $e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get a task from its user id and task id |
||||
* If userId is null, this can only get a task that was scheduled anonymously |
||||
* |
||||
* @param int $id The id of the task |
||||
* @param string|null $userId The user id that scheduled the task |
||||
* @return Task |
||||
* @throws RuntimeException If the query failed |
||||
* @throws NotFoundException If the task could not be found |
||||
*/ |
||||
public function getUserTask(int $id, ?string $userId): Task { |
||||
try { |
||||
$taskEntity = $this->taskMapper->findByIdAndUser($id, $userId); |
||||
return $taskEntity->toPublicTask(); |
||||
} catch (DoesNotExistException $e) { |
||||
throw new TaskNotFoundException('Could not find task with the provided id and user id'); |
||||
} catch (MultipleObjectsReturnedException $e) { |
||||
throw new RuntimeException('Could not uniquely identify task with given id and user id', 0, $e); |
||||
} catch (Exception $e) { |
||||
throw new RuntimeException('Failure while trying to find task by id and user id: ' . $e->getMessage(), 0, $e); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Get a list of tasks scheduled by a specific user for a specific app |
||||
* and optionally with a specific identifier. |
||||
* This cannot be used to get anonymously scheduled tasks |
||||
* |
||||
* @param string $userId |
||||
* @param string $appId |
||||
* @param string|null $identifier |
||||
* @return array |
||||
*/ |
||||
public function getUserTasksByApp(string $userId, string $appId, ?string $identifier = null): array { |
||||
try { |
||||
$taskEntities = $this->taskMapper->findUserTasksByApp($userId, $appId, $identifier); |
||||
return array_map(static function (DbTask $taskEntity) { |
||||
return $taskEntity->toPublicTask(); |
||||
}, $taskEntities); |
||||
} catch (Exception $e) { |
||||
throw new RuntimeException('Failure while trying to find tasks by appId and identifier: ' . $e->getMessage(), 0, $e); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,59 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
|
||||
namespace OC\TextToImage; |
||||
|
||||
use OC\TextToImage\Db\TaskMapper; |
||||
use OCP\AppFramework\Utility\ITimeFactory; |
||||
use OCP\BackgroundJob\TimedJob; |
||||
use OCP\DB\Exception; |
||||
use Psr\Log\LoggerInterface; |
||||
|
||||
class RemoveOldTasksBackgroundJob extends TimedJob { |
||||
public const MAX_TASK_AGE_SECONDS = 60 * 50 * 24 * 7; // 1 week |
||||
|
||||
public function __construct( |
||||
ITimeFactory $timeFactory, |
||||
private TaskMapper $taskMapper, |
||||
private LoggerInterface $logger, |
||||
|
||||
) { |
||||
parent::__construct($timeFactory); |
||||
$this->setInterval(60 * 60 * 24); |
||||
} |
||||
|
||||
/** |
||||
* @param mixed $argument |
||||
* @inheritDoc |
||||
*/ |
||||
protected function run($argument) { |
||||
try { |
||||
$this->taskMapper->deleteOlderThan(self::MAX_TASK_AGE_SECONDS); |
||||
} catch (Exception $e) { |
||||
$this->logger->warning('Failed to delete stale text to image tasks', ['exception' => $e]); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,63 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
|
||||
namespace OC\TextToImage; |
||||
|
||||
use OCP\AppFramework\Utility\ITimeFactory; |
||||
use OCP\BackgroundJob\QueuedJob; |
||||
use OCP\EventDispatcher\IEventDispatcher; |
||||
use OCP\TextToImage\Events\TaskFailedEvent; |
||||
use OCP\TextToImage\Events\TaskSuccessfulEvent; |
||||
use OCP\TextToImage\IManager; |
||||
|
||||
class TaskBackgroundJob extends QueuedJob { |
||||
public function __construct( |
||||
ITimeFactory $timeFactory, |
||||
private IManager $text2imageManager, |
||||
private IEventDispatcher $eventDispatcher, |
||||
) { |
||||
parent::__construct($timeFactory); |
||||
// We want to avoid overloading the machine with these jobs |
||||
// so we only allow running one job at a time |
||||
$this->setAllowParallelRuns(false); |
||||
} |
||||
|
||||
/** |
||||
* @param array{taskId: int} $argument |
||||
* @inheritDoc |
||||
*/ |
||||
protected function run($argument) { |
||||
$taskId = $argument['taskId']; |
||||
$task = $this->text2imageManager->getTask($taskId); |
||||
try { |
||||
$this->text2imageManager->runTask($task); |
||||
$event = new TaskSuccessfulEvent($task); |
||||
} catch (\Throwable $e) { |
||||
$event = new TaskFailedEvent($task, $e->getMessage()); |
||||
} |
||||
$this->eventDispatcher->dispatchTyped($event); |
||||
} |
||||
} |
||||
@ -0,0 +1,52 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\TextToImage\Events; |
||||
|
||||
use OCP\EventDispatcher\Event; |
||||
use OCP\TextToImage\Task; |
||||
|
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
abstract class AbstractTextToImageEvent extends Event { |
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function __construct( |
||||
private Task $task |
||||
) { |
||||
parent::__construct(); |
||||
} |
||||
|
||||
/** |
||||
* @return Task |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function getTask(): Task { |
||||
return $this->task; |
||||
} |
||||
} |
||||
@ -0,0 +1,54 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\TextToImage\Events; |
||||
|
||||
use OCP\TextToImage\Task; |
||||
|
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
class TaskFailedEvent extends AbstractTextToImageEvent { |
||||
/** |
||||
* @param Task $task |
||||
* @param string $errorMessage |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function __construct( |
||||
Task $task, |
||||
private string $errorMessage, |
||||
) { |
||||
parent::__construct($task); |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function getErrorMessage(): string { |
||||
return $this->errorMessage; |
||||
} |
||||
} |
||||
@ -0,0 +1,33 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\TextToImage\Events; |
||||
|
||||
/** |
||||
* @since 27.1.0 |
||||
*/ |
||||
class TaskSuccessfulEvent extends AbstractTextToImageEvent { |
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\TextToImage\Exception; |
||||
|
||||
class Exception extends \Exception { |
||||
|
||||
} |
||||
@ -0,0 +1,29 @@ |
||||
<?php |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
* |
||||
*/ |
||||
|
||||
namespace OCP\TextToImage\Exception; |
||||
|
||||
class TaskNotFoundException extends Exception { |
||||
|
||||
} |
||||
@ -0,0 +1,98 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
|
||||
namespace OCP\TextToImage; |
||||
|
||||
use OCP\PreConditionNotMetException; |
||||
use OCP\TextToImage\Exception\TaskNotFoundException; |
||||
use RuntimeException; |
||||
|
||||
/** |
||||
* API surface for apps interacting with and making use of TextToImage providers |
||||
* without knowing which providers are installed |
||||
* @since 28.0.0 |
||||
*/ |
||||
interface IManager { |
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function hasProviders(): bool; |
||||
|
||||
/** |
||||
* @param Task $task The task to run |
||||
* @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called |
||||
* @throws RuntimeException If something else failed |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function runTask(Task $task): void; |
||||
|
||||
/** |
||||
* Will schedule a TextToImage process in the background. The result will become available |
||||
* with the \OCP\TextToImage\TaskSuccessfulEvent |
||||
* If inference fails a \OCP\TextToImage\Events\TaskFailedEvent will be dispatched instead |
||||
* |
||||
* @param Task $task The task to schedule |
||||
* @throws PreConditionNotMetException If no or not the requested provider was registered but this method was still called |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function scheduleTask(Task $task) : void; |
||||
|
||||
/** |
||||
* Delete a task that has been scheduled before |
||||
* |
||||
* @param Task $task The task to delete |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function deleteTask(Task $task): void; |
||||
|
||||
/** |
||||
* @param int $id The id of the task |
||||
* @return Task |
||||
* @throws RuntimeException If the query failed |
||||
* @throws TaskNotFoundException If the task could not be found |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function getTask(int $id): Task; |
||||
|
||||
/** |
||||
* @param int $id The id of the task |
||||
* @param string|null $userId The user id that scheduled the task |
||||
* @return Task |
||||
* @throws RuntimeException If the query failed |
||||
* @throws TaskNotFoundException If the task could not be found |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function getUserTask(int $id, ?string $userId): Task; |
||||
|
||||
/** |
||||
* @param string $userId |
||||
* @param string $appId |
||||
* @param string|null $identifier |
||||
* @return array |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function getUserTasksByApp(string $userId, string $appId, ?string $identifier = null): array; |
||||
} |
||||
@ -0,0 +1,52 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
namespace OCP\TextToImage; |
||||
|
||||
use RuntimeException; |
||||
|
||||
/** |
||||
* This is the interface that is implemented by apps that |
||||
* implement a text to image provider |
||||
* @since 28.0.0 |
||||
*/ |
||||
interface IProvider { |
||||
/** |
||||
* The localized name of this provider |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function getName(): string; |
||||
|
||||
/** |
||||
* Processes a text |
||||
* |
||||
* @param string $prompt The input text |
||||
* @param resource $resource The file resource to write the image to |
||||
* @return void |
||||
* @since 28.0.0 |
||||
* @throws RuntimeException If the text could not be processed |
||||
*/ |
||||
public function generate(string $prompt, $resource): void; |
||||
} |
||||
@ -0,0 +1,179 @@ |
||||
<?php |
||||
|
||||
declare(strict_types=1); |
||||
|
||||
/** |
||||
* @copyright Copyright (c) 2023 Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @author Marcel Klehr <mklehr@gmx.net> |
||||
* |
||||
* @license GNU AGPL version 3 or any later version |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as |
||||
* published by the Free Software Foundation, either version 3 of the |
||||
* License, or (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
*/ |
||||
|
||||
namespace OCP\TextToImage; |
||||
|
||||
use OCP\IImage; |
||||
use OCP\Image; |
||||
use OCP\TextProcessing\IProvider as P; |
||||
use OCP\TextProcessing\ITaskType; |
||||
|
||||
/** |
||||
* This is a text to image task |
||||
* |
||||
* @since 28.0.0 |
||||
*/ |
||||
final class Task implements \JsonSerializable { |
||||
protected ?int $id = null; |
||||
|
||||
private ?IImage $image = null; |
||||
|
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public const STATUS_FAILED = 4; |
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public const STATUS_SUCCESSFUL = 3; |
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public const STATUS_RUNNING = 2; |
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public const STATUS_SCHEDULED = 1; |
||||
/** |
||||
* @since 28.0.0 |
||||
*/ |
||||
public const STATUS_UNKNOWN = 0; |
||||
|
||||
/** |
||||
* @psalm-var self::STATUS_* |
||||
*/ |
||||
protected int $status = self::STATUS_UNKNOWN; |
||||
|
||||
/** |
||||
* @param string $input |
||||
* @param string $appId |
||||
* @param string|null $userId |
||||
* @param string $identifier An arbitrary identifier for this task. max length: 255 chars |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function __construct( |
||||
protected string $input, |
||||
protected string $appId, |
||||
protected ?string $userId, |
||||
protected string $identifier = '', |
||||
) { |
||||
} |
||||
|
||||
/** |
||||
* @return IImage|null |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function getOutputImage(): ?IImage { |
||||
return $this->image; |
||||
} |
||||
|
||||
/** |
||||
* @param IImage|null $image |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function setOutputImage(?IImage $image): void { |
||||
$this->image = $image; |
||||
} |
||||
|
||||
/** |
||||
* @psalm-return self::STATUS_* |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function getStatus(): int { |
||||
return $this->status; |
||||
} |
||||
|
||||
/** |
||||
* @psalm-param self::STATUS_* $status |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function setStatus(int $status): void { |
||||
$this->status = $status; |
||||
} |
||||
|
||||
/** |
||||
* @return int|null |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function getId(): ?int { |
||||
return $this->id; |
||||
} |
||||
|
||||
/** |
||||
* @param int|null $id |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function setId(?int $id): void { |
||||
$this->id = $id; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function getInput(): string { |
||||
return $this->input; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function getAppId(): string { |
||||
return $this->appId; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function getIdentifier(): string { |
||||
return $this->identifier; |
||||
} |
||||
|
||||
/** |
||||
* @return string|null |
||||
* @since 28.0.0 |
||||
*/ |
||||
final public function getUserId(): ?string { |
||||
return $this->userId; |
||||
} |
||||
|
||||
/** |
||||
* @psalm-return array{id: ?int, status: 0|1|2|3|4, userId: ?string, appId: string, input: string, output: ?string, identifier: string} |
||||
* @since 28.0.0 |
||||
*/ |
||||
public function jsonSerialize(): array { |
||||
return [ |
||||
'id' => $this->getId(), |
||||
'status' => $this->getStatus(), |
||||
'userId' => $this->getUserId(), |
||||
'appId' => $this->getAppId(), |
||||
'input' => $this->getInput(), |
||||
'result' => $this->getOutput(), |
||||
'identifier' => $this->getIdentifier(), |
||||
]; |
||||
} |
||||
} |
||||
Loading…
Reference in new issue