Skip to content

Commit

Permalink
feat(api): File conversion API
Browse files Browse the repository at this point in the history
Signed-off-by: Elizabeth Danzberger <[email protected]>
  • Loading branch information
elzody committed Jan 15, 2025
1 parent 6da5897 commit fdfeb7f
Show file tree
Hide file tree
Showing 21 changed files with 732 additions and 2 deletions.
1 change: 1 addition & 0 deletions apps/files/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
'OCA\\Files\\Command\\ScanAppData' => $baseDir . '/../lib/Command/ScanAppData.php',
'OCA\\Files\\Command\\TransferOwnership' => $baseDir . '/../lib/Command/TransferOwnership.php',
'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
'OCA\\Files\\Controller\\ConversionApiController' => $baseDir . '/../lib/Controller/ConversionApiController.php',
'OCA\\Files\\Controller\\DirectEditingController' => $baseDir . '/../lib/Controller/DirectEditingController.php',
'OCA\\Files\\Controller\\DirectEditingViewController' => $baseDir . '/../lib/Controller/DirectEditingViewController.php',
'OCA\\Files\\Controller\\OpenLocalEditorController' => $baseDir . '/../lib/Controller/OpenLocalEditorController.php',
Expand Down
1 change: 1 addition & 0 deletions apps/files/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class ComposerStaticInitFiles
'OCA\\Files\\Command\\ScanAppData' => __DIR__ . '/..' . '/../lib/Command/ScanAppData.php',
'OCA\\Files\\Command\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Command/TransferOwnership.php',
'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
'OCA\\Files\\Controller\\ConversionApiController' => __DIR__ . '/..' . '/../lib/Controller/ConversionApiController.php',
'OCA\\Files\\Controller\\DirectEditingController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingController.php',
'OCA\\Files\\Controller\\DirectEditingViewController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingViewController.php',
'OCA\\Files\\Controller\\OpenLocalEditorController' => __DIR__ . '/..' . '/../lib/Controller/OpenLocalEditorController.php',
Expand Down
9 changes: 8 additions & 1 deletion apps/files/lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@
use OC\Files\FilenameValidator;
use OCA\Files\Service\ChunkedUploadConfig;
use OCP\Capabilities\ICapability;
use OCP\Files\Conversion\ConversionMimeTuple;
use OCP\Files\Conversion\IConversionManager;

class Capabilities implements ICapability {

public function __construct(
protected FilenameValidator $filenameValidator,
protected IConversionManager $fileConversionManager,
) {
}

/**
* Return this classes capabilities
*
* @return array{files: array{'$comment': ?string, bigfilechunking: bool, blacklisted_files: list<mixed>, forbidden_filenames: list<string>, forbidden_filename_basenames: list<string>, forbidden_filename_characters: list<string>, forbidden_filename_extensions: list<string>, chunked_upload: array{max_size: int, max_parallel_count: int}}}
* @return array{files: array{'$comment': ?string, bigfilechunking: bool, blacklisted_files: list<mixed>, forbidden_filenames: list<string>, forbidden_filename_basenames: list<string>, forbidden_filename_characters: list<string>, forbidden_filename_extensions: list<string>, chunked_upload: array{max_size: int, max_parallel_count: int}, file_conversions: list<array{from: string, to: list<array{mime: string, name: string}>}>}}
*/
public function getCapabilities(): array {
return [
Expand All @@ -38,6 +41,10 @@ public function getCapabilities(): array {
'max_size' => ChunkedUploadConfig::getMaxChunkSize(),
'max_parallel_count' => ChunkedUploadConfig::getMaxParallelCount(),
],

'file_conversions' => array_map(function (ConversionMimeTuple $mimeTuple) {
return $mimeTuple->jsonSerialize();
}, $this->fileConversionManager->getMimeTypes()),
],
];
}
Expand Down
95 changes: 95 additions & 0 deletions apps/files/lib/Controller/ConversionApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCA\Files\Controller;

use OC\Files\Utils\PathHelper;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\UserRateLimit;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSForbiddenException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\OCSController;
use OCP\Files\Conversion\IConversionManager;
use OCP\Files\File;
use OCP\Files\IRootFolder;
use OCP\IL10N;
use OCP\IRequest;

class ConversionApiController extends OCSController {
public function __construct(
string $appName,
IRequest $request,
private IConversionManager $fileConversionManager,
private IRootFolder $rootFolder,
private IL10N $l10n,
private ?string $userId,
) {
parent::__construct($appName, $request);
}

/**
* Converts a file from one MIME type to another
*
* @param int $fileId ID of the file to be converted
* @param string $targetMimeType The MIME type to which you want to convert the file
* @param string|null $destination The target path of the converted file. Written to a temporary file if left empty
*
* @return DataResponse<Http::STATUS_CREATED, array{path: string}, array{}>
*
* 201: File was converted and written to the destination or temporary file
*
* @throws OCSException The file was unable to be converted
* @throws OCSNotFoundException The file to be converted was not found
*/
#[NoAdminRequired]
#[UserRateLimit(limit: 25, period: 120)]
#[ApiRoute(verb: 'POST', url: '/api/v1/convert')]
public function convert(int $fileId, string $targetMimeType, ?string $destination = null): DataResponse {
$userFolder = $this->rootFolder->getUserFolder($this->userId);
$file = $userFolder->getFirstNodeById($fileId);

if (!($file instanceof File)) {
throw new OCSNotFoundException($this->l10n->t('The file cannot be found'));
}

if ($destination !== null) {
$destination = PathHelper::normalizePath($destination);
$parentDir = dirname($destination);

if (!$userFolder->nodeExists($parentDir)) {
throw new OCSNotFoundException($this->l10n->t('The destination path does not exist: %1$s', [$parentDir]));
}

if (!$userFolder->get($parentDir)->isCreatable()) {
throw new OCSForbiddenException();
}

$destination = $userFolder->getFullPath($destination);
}

try {
$convertedFile = $this->fileConversionManager->convert($file, $targetMimeType, $destination);
} catch (\Exception $e) {
throw new OCSException($e->getMessage());
}

$convertedFileRelativePath = $userFolder->getRelativePath($convertedFile);
if ($convertedFileRelativePath === null) {
throw new OCSNotFoundException($this->l10n->t('Could not get relative path to converted file'));
}

return new DataResponse([
'path' => $convertedFileRelativePath,
], Http::STATUS_CREATED);
}
}
149 changes: 149 additions & 0 deletions apps/files/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"forbidden_filename_characters",
"forbidden_filename_extensions",
"chunked_upload",
"file_conversions",
"directEditing"
],
"properties": {
Expand Down Expand Up @@ -94,6 +95,27 @@
}
}
},
"file_conversions": {
"type": "array",
"items": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"from": {
"type": "string"
},
"to": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
},
"directEditing": {
"type": "object",
"required": [
Expand Down Expand Up @@ -2221,6 +2243,133 @@
}
}
}
},
"/ocs/v2.php/apps/files/api/v1/convert": {
"post": {
"operationId": "conversion_api-convert",
"summary": "Converts a file from one MIME type to another",
"tags": [
"conversion_api"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"fileId",
"targetMimeType"
],
"properties": {
"fileId": {
"type": "integer",
"format": "int64",
"description": "ID of the file to be converted"
},
"targetMimeType": {
"type": "string",
"description": "The MIME type to which you want to convert the file"
},
"destination": {
"type": "string",
"nullable": true,
"description": "The target path of the converted file. Written to a temporary file if left empty"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"201": {
"description": "File was converted and written to the destination or temporary file",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"path"
],
"properties": {
"path": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "The file to be converted was not found",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
}
},
"tags": []
Expand Down
Loading

0 comments on commit fdfeb7f

Please sign in to comment.