diff --git a/core/Controller/ConversionApiController.php b/core/Controller/ConversionApiController.php new file mode 100644 index 0000000000000..66900ef0b4840 --- /dev/null +++ b/core/Controller/ConversionApiController.php @@ -0,0 +1,59 @@ +rootFolder->getUserFolder($this->userId); + $file = $userFolder->getFirstNodeById($fileId); + + if (!$file || !($file instanceof File)) { + return new DataResponse([ + 'message' => $this->l10n->t('File not found'), + ], Http::STATUS_NOT_FOUND); + } + + try { + // TODO: Need more discussion around how to proceed + $conversion = $this->conversionManager->convert($file, $targetMimeType); + } catch (\Exception $e) { + return new DataResponse([ + 'message' => $e->getMessage(), + ], Http::STATUS_INTERNAL_SERVER_ERROR); + } + + return new DataResponse([], Http::STATUS_OK); + } +} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 7064b97f430d4..bc5dea9883a5b 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -264,6 +264,9 @@ 'OCP\\Contacts\\ContactsMenu\\IProvider' => $baseDir . '/lib/public/Contacts/ContactsMenu/IProvider.php', 'OCP\\Contacts\\Events\\ContactInteractedWithEvent' => $baseDir . '/lib/public/Contacts/Events/ContactInteractedWithEvent.php', 'OCP\\Contacts\\IManager' => $baseDir . '/lib/public/Contacts/IManager.php', + 'OCP\\Conversion\\ConversionMimeTuple' => $baseDir . '/lib/public/Conversion/ConversionMimeTuple.php', + 'OCP\\Conversion\\IConversionManager' => $baseDir . '/lib/public/Conversion/IConversionManager.php', + 'OCP\\Conversion\\IConversionProvider' => $baseDir . '/lib/public/Conversion/IConversionProvider.php', 'OCP\\DB\\Events\\AddMissingColumnsEvent' => $baseDir . '/lib/public/DB/Events/AddMissingColumnsEvent.php', 'OCP\\DB\\Events\\AddMissingIndicesEvent' => $baseDir . '/lib/public/DB/Events/AddMissingIndicesEvent.php', 'OCP\\DB\\Events\\AddMissingPrimaryKeyEvent' => $baseDir . '/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php', @@ -1165,6 +1168,7 @@ 'OC\\Contacts\\ContactsMenu\\Providers\\EMailProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php', 'OC\\Contacts\\ContactsMenu\\Providers\\LocalTimeProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/LocalTimeProvider.php', 'OC\\Contacts\\ContactsMenu\\Providers\\ProfileProvider' => $baseDir . '/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php', + 'OC\\Conversion\\ConversionManager' => $baseDir . '/lib/private/Conversion/ConversionManager.php', 'OC\\Core\\Application' => $baseDir . '/core/Application.php', 'OC\\Core\\BackgroundJobs\\BackgroundCleanupUpdaterBackupsJob' => $baseDir . '/core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php', 'OC\\Core\\BackgroundJobs\\CheckForUserCertificates' => $baseDir . '/core/BackgroundJobs/CheckForUserCertificates.php', @@ -1303,6 +1307,7 @@ 'OC\\Core\\Controller\\ClientFlowLoginV2Controller' => $baseDir . '/core/Controller/ClientFlowLoginV2Controller.php', 'OC\\Core\\Controller\\CollaborationResourcesController' => $baseDir . '/core/Controller/CollaborationResourcesController.php', 'OC\\Core\\Controller\\ContactsMenuController' => $baseDir . '/core/Controller/ContactsMenuController.php', + 'OC\\Core\\Controller\\ConversionApiController' => $baseDir . '/core/Controller/ConversionApiController.php', 'OC\\Core\\Controller\\CssController' => $baseDir . '/core/Controller/CssController.php', 'OC\\Core\\Controller\\ErrorController' => $baseDir . '/core/Controller/ErrorController.php', 'OC\\Core\\Controller\\GuestAvatarController' => $baseDir . '/core/Controller/GuestAvatarController.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index adb0bf6899b4e..05636e18046ea 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -313,6 +313,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Contacts\\ContactsMenu\\IProvider' => __DIR__ . '/../../..' . '/lib/public/Contacts/ContactsMenu/IProvider.php', 'OCP\\Contacts\\Events\\ContactInteractedWithEvent' => __DIR__ . '/../../..' . '/lib/public/Contacts/Events/ContactInteractedWithEvent.php', 'OCP\\Contacts\\IManager' => __DIR__ . '/../../..' . '/lib/public/Contacts/IManager.php', + 'OCP\\Conversion\\ConversionMimeTuple' => __DIR__ . '/../../..' . '/lib/public/Conversion/ConversionMimeTuple.php', + 'OCP\\Conversion\\IConversionManager' => __DIR__ . '/../../..' . '/lib/public/Conversion/IConversionManager.php', + 'OCP\\Conversion\\IConversionProvider' => __DIR__ . '/../../..' . '/lib/public/Conversion/IConversionProvider.php', 'OCP\\DB\\Events\\AddMissingColumnsEvent' => __DIR__ . '/../../..' . '/lib/public/DB/Events/AddMissingColumnsEvent.php', 'OCP\\DB\\Events\\AddMissingIndicesEvent' => __DIR__ . '/../../..' . '/lib/public/DB/Events/AddMissingIndicesEvent.php', 'OCP\\DB\\Events\\AddMissingPrimaryKeyEvent' => __DIR__ . '/../../..' . '/lib/public/DB/Events/AddMissingPrimaryKeyEvent.php', @@ -1214,6 +1217,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Contacts\\ContactsMenu\\Providers\\EMailProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php', 'OC\\Contacts\\ContactsMenu\\Providers\\LocalTimeProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/LocalTimeProvider.php', 'OC\\Contacts\\ContactsMenu\\Providers\\ProfileProvider' => __DIR__ . '/../../..' . '/lib/private/Contacts/ContactsMenu/Providers/ProfileProvider.php', + 'OC\\Conversion\\ConversionManager' => __DIR__ . '/../../..' . '/lib/private/Conversion/ConversionManager.php', 'OC\\Core\\Application' => __DIR__ . '/../../..' . '/core/Application.php', 'OC\\Core\\BackgroundJobs\\BackgroundCleanupUpdaterBackupsJob' => __DIR__ . '/../../..' . '/core/BackgroundJobs/BackgroundCleanupUpdaterBackupsJob.php', 'OC\\Core\\BackgroundJobs\\CheckForUserCertificates' => __DIR__ . '/../../..' . '/core/BackgroundJobs/CheckForUserCertificates.php', @@ -1352,6 +1356,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Controller\\ClientFlowLoginV2Controller' => __DIR__ . '/../../..' . '/core/Controller/ClientFlowLoginV2Controller.php', 'OC\\Core\\Controller\\CollaborationResourcesController' => __DIR__ . '/../../..' . '/core/Controller/CollaborationResourcesController.php', 'OC\\Core\\Controller\\ContactsMenuController' => __DIR__ . '/../../..' . '/core/Controller/ContactsMenuController.php', + 'OC\\Core\\Controller\\ConversionApiController' => __DIR__ . '/../../..' . '/core/Controller/ConversionApiController.php', 'OC\\Core\\Controller\\CssController' => __DIR__ . '/../../..' . '/core/Controller/CssController.php', 'OC\\Core\\Controller\\ErrorController' => __DIR__ . '/../../..' . '/core/Controller/ErrorController.php', 'OC\\Core\\Controller\\GuestAvatarController' => __DIR__ . '/../../..' . '/core/Controller/GuestAvatarController.php', diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index f3b612edc38bb..8790caf253626 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -153,6 +153,9 @@ class RegistrationContext { /** @var ServiceRegistration<\OCP\TaskProcessing\ITaskType>[] */ private array $taskProcessingTaskTypes = []; + + /** @var ServiceRegistration<\OCP\Conversion\IConversionProvider>[] */ + private array $conversionProviders = []; /** @var ServiceRegistration[] */ private $mailProviders = []; @@ -420,6 +423,13 @@ public function registerTaskProcessingTaskType(string $taskProcessingTaskTypeCla ); } + public function registerConversionProvider(string $conversionProviderClass): void { + $this->context->registerConversionProvider( + $this->appId, + $conversionProviderClass + ); + } + public function registerMailProvider(string $class): void { $this->context->registerMailProvider( $this->appId, @@ -625,6 +635,14 @@ public function registerTaskProcessingProvider(string $appId, string $taskProces public function registerTaskProcessingTaskType(string $appId, string $taskProcessingTaskTypeClass) { $this->taskProcessingTaskTypes[] = new ServiceRegistration($appId, $taskProcessingTaskTypeClass); } + + /** + * @psalm-param class-string<\OCP\Conversion\IConversionProvider> $conversionProviderClass + */ + public function registerConversionProvider(string $appId, string $conversionProviderClass): void { + $this->conversionProviders[] = new ServiceRegistration($appId, $conversionProviderClass); + } + /** * @psalm-param class-string $migratorClass */ @@ -984,6 +1002,13 @@ public function getTaskProcessingTaskTypes(): array { return $this->taskProcessingTaskTypes; } + /** + * @return ServiceRegistration<\OCP\Conversion\IConversionProvider>[] + */ + public function getConversionProviders(): array { + return $this->conversionProviders; + } + /** * @return ServiceRegistration[] */ diff --git a/lib/private/Conversion/ConversionManager.php b/lib/private/Conversion/ConversionManager.php new file mode 100644 index 0000000000000..16f67583f38d9 --- /dev/null +++ b/lib/private/Conversion/ConversionManager.php @@ -0,0 +1,97 @@ +coordinator->getRegistrationContext(); + return !empty($context->getConversionProviders()); + } + + public function getMimeTypes(): array { + $mimeTypes = []; + + foreach ($this->getProviders() as $provider) { + $mimeTypes[] = $provider->getSupportedMimeTypes(); + } + + return $mimeTypes; + } + + public function convert(File $file, string $targetMimeType): File { + if (!$this->hasProviders()) { + throw new PreConditionNotMetException('No conversion providers available'); + } + + $fileMimeType = $file->getMimetype(); + foreach ($this->getProviders() as $provider) { + $availableProviderConversions = array_filter( + $provider->getSupportedMimeTypes(), + function (ConversionMimeTuple $mimeTuple) use ($fileMimeType, $targetMimeType) { + ['from' => $from, 'to' => $to] = $mimeTuple->jsonSerialize(); + + return $from === $fileMimeType && $to === $targetMimeType; + } + ); + + if (!empty($availableProviderConversions)) { + return $provider->convertFile($file, $targetMimeType); + } + } + + throw new RuntimeException('Could not convert file'); + } + + public function getProviders(): array { + if ($this->providers !== null) { + return $this->providers; + } + + $context = $this->coordinator->getRegistrationContext(); + $this->providers = []; + + foreach ($context->getConversionProviders() as $providerRegistration) { + $class = $providerRegistration->getService(); + + try { + $this->providers[$class] = $this->serverContainer->get($class); + } catch (NotFoundExceptionInterface|ContainerExceptionInterface|Throwable $e) { + $this->logger->error('Failed to load conversion provider ' . $class, [ + 'exception' => $e, + ]); + } + } + + return $this->providers; + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index a20c37732a76e..9f38477be0cb3 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -34,6 +34,7 @@ use OC\Comments\ManagerFactory as CommentsManagerFactory; use OC\Contacts\ContactsMenu\ActionFactory; use OC\Contacts\ContactsMenu\ContactsStore; +use OC\Conversion\ConversionManager; use OC\DB\Connection; use OC\DB\ConnectionAdapter; use OC\Diagnostics\EventLogger; @@ -143,6 +144,7 @@ use OCP\Comments\ICommentsManager; use OCP\Contacts\ContactsMenu\IActionFactory; use OCP\Contacts\ContactsMenu\IContactsStore; +use OCP\Conversion\IConversionManager; use OCP\Defaults; use OCP\Diagnostics\IEventLogger; use OCP\Diagnostics\IQueryLogger; @@ -1258,6 +1260,8 @@ public function __construct($webRoot, \OC\Config $config) { $this->registerAlias(ITranslationManager::class, TranslationManager::class); + $this->registerAlias(IConversionManager::class, ConversionManager::class); + $this->registerAlias(ISpeechToTextManager::class, SpeechToTextManager::class); $this->registerAlias(IEventSourceFactory::class, EventSourceFactory::class); diff --git a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php index 8a18ec8ae9da0..6a3ed93f12a83 100644 --- a/lib/public/AppFramework/Bootstrap/IRegistrationContext.php +++ b/lib/public/AppFramework/Bootstrap/IRegistrationContext.php @@ -414,6 +414,19 @@ public function registerTaskProcessingProvider(string $taskProcessingProviderCla */ public function registerTaskProcessingTaskType(string $taskProcessingTaskTypeClass): void; + /** + * Register an implementation of \OCP\Conversion\IConversionProvider + * that will handle the conversion of files from one MIME type to another + * + * @param string $conversionProviderClass + * @psalm-param class-string<\OCP\Conversion\IConversionProvider> $conversionProviderClass + * + * @return void + * + * @since 31.0.0 + */ + public function registerConversionProvider(string $conversionProviderClass): void; + /** * Register a mail provider * diff --git a/lib/public/Conversion/ConversionMimeTuple.php b/lib/public/Conversion/ConversionMimeTuple.php new file mode 100644 index 0000000000000..ef2deaa732b94 --- /dev/null +++ b/lib/public/Conversion/ConversionMimeTuple.php @@ -0,0 +1,39 @@ + $this->from, + 'to' => $this->to, + ]; + } +} diff --git a/lib/public/Conversion/IConversionManager.php b/lib/public/Conversion/IConversionManager.php new file mode 100644 index 0000000000000..3b5a2fcf83656 --- /dev/null +++ b/lib/public/Conversion/IConversionManager.php @@ -0,0 +1,47 @@ + + * + * @since 31.0.0 + */ + public function getMimeTypes(): array; + + /** + * Convert a file to a given MIME type + * + * @param File $file The file to be converted + * @param string $targetMimeType The MIME type to convert the file to + * + * @return File + * + * @since 31.0.0 + */ + public function convert(File $file, string $targetMimeType): File; +} diff --git a/lib/public/Conversion/IConversionProvider.php b/lib/public/Conversion/IConversionProvider.php new file mode 100644 index 0000000000000..b534fdc560cdc --- /dev/null +++ b/lib/public/Conversion/IConversionProvider.php @@ -0,0 +1,50 @@ + + * + * @since 31.0.0 + */ + public function getSupportedMimeTypes(): array; + + /** + * Convert a file to a given MIME type + * + * @param File $file The file to be converted + * @param string $targetMimeType The MIME type to convert the file to + * + * @return File + * + * @since 31.0.0 + */ + public function convertFile(File $file, string $targetMimeType): File; +}