Skip to content

Commit fdfeb7f

Browse files
committed
feat(api): File conversion API
Signed-off-by: Elizabeth Danzberger <lizzy7128@tutanota.de>
1 parent 6da5897 commit fdfeb7f

File tree

21 files changed

+732
-2
lines changed

21 files changed

+732
-2
lines changed

apps/files/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
'OCA\\Files\\Command\\ScanAppData' => $baseDir . '/../lib/Command/ScanAppData.php',
4343
'OCA\\Files\\Command\\TransferOwnership' => $baseDir . '/../lib/Command/TransferOwnership.php',
4444
'OCA\\Files\\Controller\\ApiController' => $baseDir . '/../lib/Controller/ApiController.php',
45+
'OCA\\Files\\Controller\\ConversionApiController' => $baseDir . '/../lib/Controller/ConversionApiController.php',
4546
'OCA\\Files\\Controller\\DirectEditingController' => $baseDir . '/../lib/Controller/DirectEditingController.php',
4647
'OCA\\Files\\Controller\\DirectEditingViewController' => $baseDir . '/../lib/Controller/DirectEditingViewController.php',
4748
'OCA\\Files\\Controller\\OpenLocalEditorController' => $baseDir . '/../lib/Controller/OpenLocalEditorController.php',

apps/files/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class ComposerStaticInitFiles
5757
'OCA\\Files\\Command\\ScanAppData' => __DIR__ . '/..' . '/../lib/Command/ScanAppData.php',
5858
'OCA\\Files\\Command\\TransferOwnership' => __DIR__ . '/..' . '/../lib/Command/TransferOwnership.php',
5959
'OCA\\Files\\Controller\\ApiController' => __DIR__ . '/..' . '/../lib/Controller/ApiController.php',
60+
'OCA\\Files\\Controller\\ConversionApiController' => __DIR__ . '/..' . '/../lib/Controller/ConversionApiController.php',
6061
'OCA\\Files\\Controller\\DirectEditingController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingController.php',
6162
'OCA\\Files\\Controller\\DirectEditingViewController' => __DIR__ . '/..' . '/../lib/Controller/DirectEditingViewController.php',
6263
'OCA\\Files\\Controller\\OpenLocalEditorController' => __DIR__ . '/..' . '/../lib/Controller/OpenLocalEditorController.php',

apps/files/lib/Capabilities.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,21 @@
1010
use OC\Files\FilenameValidator;
1111
use OCA\Files\Service\ChunkedUploadConfig;
1212
use OCP\Capabilities\ICapability;
13+
use OCP\Files\Conversion\ConversionMimeTuple;
14+
use OCP\Files\Conversion\IConversionManager;
1315

1416
class Capabilities implements ICapability {
1517

1618
public function __construct(
1719
protected FilenameValidator $filenameValidator,
20+
protected IConversionManager $fileConversionManager,
1821
) {
1922
}
2023

2124
/**
2225
* Return this classes capabilities
2326
*
24-
* @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}}}
27+
* @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}>}>}}
2528
*/
2629
public function getCapabilities(): array {
2730
return [
@@ -38,6 +41,10 @@ public function getCapabilities(): array {
3841
'max_size' => ChunkedUploadConfig::getMaxChunkSize(),
3942
'max_parallel_count' => ChunkedUploadConfig::getMaxParallelCount(),
4043
],
44+
45+
'file_conversions' => array_map(function (ConversionMimeTuple $mimeTuple) {
46+
return $mimeTuple->jsonSerialize();
47+
}, $this->fileConversionManager->getMimeTypes()),
4148
],
4249
];
4350
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCA\Files\Controller;
11+
12+
use OC\Files\Utils\PathHelper;
13+
use OCP\AppFramework\Http;
14+
use OCP\AppFramework\Http\Attribute\ApiRoute;
15+
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
16+
use OCP\AppFramework\Http\Attribute\UserRateLimit;
17+
use OCP\AppFramework\Http\DataResponse;
18+
use OCP\AppFramework\OCS\OCSException;
19+
use OCP\AppFramework\OCS\OCSForbiddenException;
20+
use OCP\AppFramework\OCS\OCSNotFoundException;
21+
use OCP\AppFramework\OCSController;
22+
use OCP\Files\Conversion\IConversionManager;
23+
use OCP\Files\File;
24+
use OCP\Files\IRootFolder;
25+
use OCP\IL10N;
26+
use OCP\IRequest;
27+
28+
class ConversionApiController extends OCSController {
29+
public function __construct(
30+
string $appName,
31+
IRequest $request,
32+
private IConversionManager $fileConversionManager,
33+
private IRootFolder $rootFolder,
34+
private IL10N $l10n,
35+
private ?string $userId,
36+
) {
37+
parent::__construct($appName, $request);
38+
}
39+
40+
/**
41+
* Converts a file from one MIME type to another
42+
*
43+
* @param int $fileId ID of the file to be converted
44+
* @param string $targetMimeType The MIME type to which you want to convert the file
45+
* @param string|null $destination The target path of the converted file. Written to a temporary file if left empty
46+
*
47+
* @return DataResponse<Http::STATUS_CREATED, array{path: string}, array{}>
48+
*
49+
* 201: File was converted and written to the destination or temporary file
50+
*
51+
* @throws OCSException The file was unable to be converted
52+
* @throws OCSNotFoundException The file to be converted was not found
53+
*/
54+
#[NoAdminRequired]
55+
#[UserRateLimit(limit: 25, period: 120)]
56+
#[ApiRoute(verb: 'POST', url: '/api/v1/convert')]
57+
public function convert(int $fileId, string $targetMimeType, ?string $destination = null): DataResponse {
58+
$userFolder = $this->rootFolder->getUserFolder($this->userId);
59+
$file = $userFolder->getFirstNodeById($fileId);
60+
61+
if (!($file instanceof File)) {
62+
throw new OCSNotFoundException($this->l10n->t('The file cannot be found'));
63+
}
64+
65+
if ($destination !== null) {
66+
$destination = PathHelper::normalizePath($destination);
67+
$parentDir = dirname($destination);
68+
69+
if (!$userFolder->nodeExists($parentDir)) {
70+
throw new OCSNotFoundException($this->l10n->t('The destination path does not exist: %1$s', [$parentDir]));
71+
}
72+
73+
if (!$userFolder->get($parentDir)->isCreatable()) {
74+
throw new OCSForbiddenException();
75+
}
76+
77+
$destination = $userFolder->getFullPath($destination);
78+
}
79+
80+
try {
81+
$convertedFile = $this->fileConversionManager->convert($file, $targetMimeType, $destination);
82+
} catch (\Exception $e) {
83+
throw new OCSException($e->getMessage());
84+
}
85+
86+
$convertedFileRelativePath = $userFolder->getRelativePath($convertedFile);
87+
if ($convertedFileRelativePath === null) {
88+
throw new OCSNotFoundException($this->l10n->t('Could not get relative path to converted file'));
89+
}
90+
91+
return new DataResponse([
92+
'path' => $convertedFileRelativePath,
93+
], Http::STATUS_CREATED);
94+
}
95+
}

apps/files/openapi.json

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"forbidden_filename_characters",
3838
"forbidden_filename_extensions",
3939
"chunked_upload",
40+
"file_conversions",
4041
"directEditing"
4142
],
4243
"properties": {
@@ -94,6 +95,27 @@
9495
}
9596
}
9697
},
98+
"file_conversions": {
99+
"type": "array",
100+
"items": {
101+
"type": "object",
102+
"required": [
103+
"from",
104+
"to"
105+
],
106+
"properties": {
107+
"from": {
108+
"type": "string"
109+
},
110+
"to": {
111+
"type": "object",
112+
"additionalProperties": {
113+
"type": "string"
114+
}
115+
}
116+
}
117+
}
118+
},
97119
"directEditing": {
98120
"type": "object",
99121
"required": [
@@ -2221,6 +2243,133 @@
22212243
}
22222244
}
22232245
}
2246+
},
2247+
"/ocs/v2.php/apps/files/api/v1/convert": {
2248+
"post": {
2249+
"operationId": "conversion_api-convert",
2250+
"summary": "Converts a file from one MIME type to another",
2251+
"tags": [
2252+
"conversion_api"
2253+
],
2254+
"security": [
2255+
{
2256+
"bearer_auth": []
2257+
},
2258+
{
2259+
"basic_auth": []
2260+
}
2261+
],
2262+
"requestBody": {
2263+
"required": true,
2264+
"content": {
2265+
"application/json": {
2266+
"schema": {
2267+
"type": "object",
2268+
"required": [
2269+
"fileId",
2270+
"targetMimeType"
2271+
],
2272+
"properties": {
2273+
"fileId": {
2274+
"type": "integer",
2275+
"format": "int64",
2276+
"description": "ID of the file to be converted"
2277+
},
2278+
"targetMimeType": {
2279+
"type": "string",
2280+
"description": "The MIME type to which you want to convert the file"
2281+
},
2282+
"destination": {
2283+
"type": "string",
2284+
"nullable": true,
2285+
"description": "The target path of the converted file. Written to a temporary file if left empty"
2286+
}
2287+
}
2288+
}
2289+
}
2290+
}
2291+
},
2292+
"parameters": [
2293+
{
2294+
"name": "OCS-APIRequest",
2295+
"in": "header",
2296+
"description": "Required to be true for the API request to pass",
2297+
"required": true,
2298+
"schema": {
2299+
"type": "boolean",
2300+
"default": true
2301+
}
2302+
}
2303+
],
2304+
"responses": {
2305+
"201": {
2306+
"description": "File was converted and written to the destination or temporary file",
2307+
"content": {
2308+
"application/json": {
2309+
"schema": {
2310+
"type": "object",
2311+
"required": [
2312+
"ocs"
2313+
],
2314+
"properties": {
2315+
"ocs": {
2316+
"type": "object",
2317+
"required": [
2318+
"meta",
2319+
"data"
2320+
],
2321+
"properties": {
2322+
"meta": {
2323+
"$ref": "#/components/schemas/OCSMeta"
2324+
},
2325+
"data": {
2326+
"type": "object",
2327+
"required": [
2328+
"path"
2329+
],
2330+
"properties": {
2331+
"path": {
2332+
"type": "string"
2333+
}
2334+
}
2335+
}
2336+
}
2337+
}
2338+
}
2339+
}
2340+
}
2341+
}
2342+
},
2343+
"404": {
2344+
"description": "The file to be converted was not found",
2345+
"content": {
2346+
"application/json": {
2347+
"schema": {
2348+
"type": "object",
2349+
"required": [
2350+
"ocs"
2351+
],
2352+
"properties": {
2353+
"ocs": {
2354+
"type": "object",
2355+
"required": [
2356+
"meta",
2357+
"data"
2358+
],
2359+
"properties": {
2360+
"meta": {
2361+
"$ref": "#/components/schemas/OCSMeta"
2362+
},
2363+
"data": {}
2364+
}
2365+
}
2366+
}
2367+
}
2368+
}
2369+
}
2370+
}
2371+
}
2372+
}
22242373
}
22252374
},
22262375
"tags": []

0 commit comments

Comments
 (0)