Skip to content

Commit

Permalink
fix(files): conversion api simplification and conflict check
Browse files Browse the repository at this point in the history
Signed-off-by: skjnldsv <[email protected]>
  • Loading branch information
skjnldsv committed Jan 16, 2025
1 parent b2d1df7 commit 85bea72
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 93 deletions.
10 changes: 5 additions & 5 deletions apps/files/lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use OC\Files\FilenameValidator;
use OCA\Files\Service\ChunkedUploadConfig;
use OCP\Capabilities\ICapability;
use OCP\Files\Conversion\ConversionMimeTuple;
use OCP\Files\Conversion\ConversionMimeProvider;
use OCP\Files\Conversion\IConversionManager;

class Capabilities implements ICapability {
Expand All @@ -24,7 +24,7 @@ public function __construct(
/**
* 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}, file_conversions: list<array{from: string, to: list<array{mime: string, name: string}>}>}}
* @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: string, extension: string, displayName: string}>}}
*/
public function getCapabilities(): array {
return [
Expand All @@ -42,9 +42,9 @@ public function getCapabilities(): array {
'max_parallel_count' => ChunkedUploadConfig::getMaxParallelCount(),
],

'file_conversions' => array_map(function (ConversionMimeTuple $mimeTuple) {
return $mimeTuple->jsonSerialize();
}, $this->fileConversionManager->getMimeTypes()),
'file_conversions' => array_map(function (ConversionMimeProvider $mimeProvider) {
return $mimeProvider->jsonSerialize();
}, $this->fileConversionManager->getProviders()),
],
];
}
Expand Down
19 changes: 13 additions & 6 deletions apps/testing/lib/Conversion/ConversionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace OCA\Testing\Conversion;

use OCP\Files\Conversion\ConversionMimeTuple;
use OCP\Files\Conversion\ConversionMimeProvider;
use OCP\Files\Conversion\IConversionProvider;
use OCP\Files\File;
use OCP\IL10N;
Expand All @@ -22,19 +22,26 @@ public function __construct(

public function getSupportedMimeTypes(): array {
return [
new ConversionMimeTuple('image/jpeg', [
['mime' => 'image/png', 'name' => $this->l10n->t('Image (.png)')],
])
new ConversionMimeProvider('image/jpeg', 'image/png', 'png', $this->l10n->t('Image (.png)')),
new ConversionMimeProvider('image/jpeg', 'image/gif', 'gif', $this->l10n->t('Image (.gif)')),
];
}

public function convertFile(File $file, string $targetMimeType): mixed {
$image = imagecreatefromstring($file->getContent());

imagepalettetotruecolor($image);

// Start output buffering
ob_start();
imagepng($image);

// Convert the image to the target format
if ($targetMimeType === 'image/gif') {
imagegif($image);
} else {
imagepng($image);
}

// End and return the output buffer
return ob_get_clean();
}
}
2 changes: 1 addition & 1 deletion lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@
'OCP\\Files\\Config\\IRootMountProvider' => $baseDir . '/lib/public/Files/Config/IRootMountProvider.php',
'OCP\\Files\\Config\\IUserMountCache' => $baseDir . '/lib/public/Files/Config/IUserMountCache.php',
'OCP\\Files\\ConnectionLostException' => $baseDir . '/lib/public/Files/ConnectionLostException.php',
'OCP\\Files\\Conversion\\ConversionMimeTuple' => $baseDir . '/lib/public/Files/Conversion/ConversionMimeTuple.php',
'OCP\\Files\\Conversion\\ConversionMimeProvider' => $baseDir . '/lib/public/Files/Conversion/ConversionMimeProvider.php',
'OCP\\Files\\Conversion\\IConversionManager' => $baseDir . '/lib/public/Files/Conversion/IConversionManager.php',
'OCP\\Files\\Conversion\\IConversionProvider' => $baseDir . '/lib/public/Files/Conversion/IConversionProvider.php',
'OCP\\Files\\DavUtil' => $baseDir . '/lib/public/Files/DavUtil.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_psr4.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
'OC\\' => array($baseDir . '/lib/private'),
'OCP\\' => array($baseDir . '/lib/public'),
'NCU\\' => array($baseDir . '/lib/unstable'),
'Bamarni\\Composer\\Bin\\' => array($vendorDir . '/bamarni/composer-bin-plugin/src'),
'' => array($baseDir . '/lib/private/legacy'),
);
10 changes: 9 additions & 1 deletion lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
array (
'NCU\\' => 4,
),
'B' =>
array (
'Bamarni\\Composer\\Bin\\' => 21,
),
);

public static $prefixDirsPsr4 = array (
Expand All @@ -40,6 +44,10 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
array (
0 => __DIR__ . '/../../..' . '/lib/unstable',
),
'Bamarni\\Composer\\Bin\\' =>
array (
0 => __DIR__ . '/..' . '/bamarni/composer-bin-plugin/src',
),
);

public static $fallbackDirsPsr4 = array (
Expand Down Expand Up @@ -418,7 +426,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Files\\Config\\IRootMountProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IRootMountProvider.php',
'OCP\\Files\\Config\\IUserMountCache' => __DIR__ . '/../../..' . '/lib/public/Files/Config/IUserMountCache.php',
'OCP\\Files\\ConnectionLostException' => __DIR__ . '/../../..' . '/lib/public/Files/ConnectionLostException.php',
'OCP\\Files\\Conversion\\ConversionMimeTuple' => __DIR__ . '/../../..' . '/lib/public/Files/Conversion/ConversionMimeTuple.php',
'OCP\\Files\\Conversion\\ConversionMimeProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Conversion/ConversionMimeProvider.php',
'OCP\\Files\\Conversion\\IConversionManager' => __DIR__ . '/../../..' . '/lib/public/Files/Conversion/IConversionManager.php',
'OCP\\Files\\Conversion\\IConversionProvider' => __DIR__ . '/../../..' . '/lib/public/Files/Conversion/IConversionProvider.php',
'OCP\\Files\\DavUtil' => __DIR__ . '/../../..' . '/lib/public/Files/DavUtil.php',
Expand Down
92 changes: 59 additions & 33 deletions lib/private/Files/Conversion/ConversionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@

use OC\AppFramework\Bootstrap\Coordinator;
use OC\SystemConfig;
use OCP\Files\Conversion\ConversionMimeTuple;
use OCP\Files\Conversion\ConversionMimeProvider;
use OCP\Files\Conversion\IConversionManager;
use OCP\Files\Conversion\IConversionProvider;
use OCP\Files\File;
use OCP\Files\Folder;
use OCP\Files\GenericFileException;
use OCP\Files\IRootFolder;
use OCP\ITempManager;
Expand Down Expand Up @@ -53,16 +54,28 @@ public function hasProviders(): bool {
return !empty($context->getFileConversionProviders());
}

public function getMimeTypes(): array {
$mimeTypes = [];

foreach ($this->getProviders() as $provider) {
$mimeTypes[] = $provider->getSupportedMimetypes();
public function getProviders(): array {
$providers = [];
foreach ($this->getRegisteredProviders() as $provider) {
$providers = array_merge($providers, $provider->getSupportedMimeTypes());
}
return $providers;
}

/**
* @param string $mime
* @return list<ConversionMimeProvider>
*/
private function getProvidersForMime(string $mime): array {
$mimeTypes = $this->getProviders();
$filtered = array_filter(
$mimeTypes,
function (ConversionMimeProvider $mimeProvider) use ($mime) {
return $mimeProvider->getFrom() === $mime;
}
);

/** @var list<ConversionMimeTuple> */
$mimeTypes = array_merge(...$mimeTypes);
return $mimeTypes;
return array_values($filtered);
}

public function convert(File $file, string $targetMimeType, ?string $destination = null): string {
Expand All @@ -80,24 +93,36 @@ public function convert(File $file, string $targetMimeType, ?string $destination
$fileMimeType = $file->getMimetype();
$validProvider = $this->getValidProvider($fileMimeType, $targetMimeType);

$targetExtension = '';
foreach ($this->getProvidersForMime($fileMimeType) as $mimeProvider) {
if ($mimeProvider->getTo() === $targetMimeType) {
$targetExtension = $mimeProvider->getExtension();
break;
}
}

if ($validProvider !== null) {
$convertedFile = $validProvider->convertFile($file, $targetMimeType);

if ($destination !== null) {
$convertedFile = $this->writeToDestination($destination, $convertedFile);
return $convertedFile->getPath();
// If destination not provided, we use the same path
// as the original file, but with the new extension
if ($destination === null) {
$basename = pathinfo($file->getPath(), PATHINFO_FILENAME);
$parent = $file->getParent();
$destination = $parent->getFullPath($basename . '.' . $targetExtension);
}

$tmp = $this->tempManager->getTemporaryFile();
file_put_contents($tmp, $convertedFile);

return $tmp;
$convertedFile = $this->writeToDestination($destination, $convertedFile);
return $convertedFile->getPath();
}

throw new RuntimeException('Could not convert file');
}

public function getProviders(): array {
/**
* @return IConversionProvider[]
*/
private function getRegisteredProviders(): array {
if (count($this->providers) > 0) {
return $this->providers;
}
Expand Down Expand Up @@ -125,28 +150,29 @@ public function getProviders(): array {
}

private function writeToDestination(string $destination, mixed $content): File {
if ($this->rootFolder->nodeExists($destination)) {
$file = $this->rootFolder->get($destination);
$parent = $file->getParent();
if (!$parent->isCreatable()) {
throw new GenericFileException('Destination is not creatable');
}

$newName = $parent->getNonExistingName(basename($destination));
$destination = $parent->getFullPath($newName);
}

return $this->rootFolder->newFile($destination, $content);
}

private function getValidProvider(string $fileMimeType, string $targetMimeType): ?IConversionProvider {
$validProvider = null;
foreach ($this->getProviders() as $provider) {
$suitableMimeTypes = array_filter(
$provider->getSupportedMimeTypes(),
function (ConversionMimeTuple $mimeTuple) use ($fileMimeType, $targetMimeType) {
['from' => $from, 'to' => $to] = $mimeTuple->jsonSerialize();

$supportsTargetMimeType = in_array($targetMimeType, array_column($to, 'mime'));
return ($from === $fileMimeType) && $supportsTargetMimeType;
foreach ($this->getRegisteredProviders() as $provider) {
foreach ($provider->getSupportedMimeTypes() as $mimeProvider) {
if ($mimeProvider->getFrom() === $fileMimeType && $mimeProvider->getTo() === $targetMimeType) {
return $provider;
}
);

if (!empty($suitableMimeTypes)) {
$validProvider = $provider;
break;
}
}

return $validProvider;
return null;
}
}
66 changes: 66 additions & 0 deletions lib/public/Files/Conversion/ConversionMimeProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

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

namespace OCP\Files\Conversion;

use JsonSerializable;

/**
* A tuple-like object representing both an original and target
* MIME type for a file conversion
*
* @since 31.0.0
*/
class ConversionMimeProvider implements JsonSerializable {
/**
* @param string $from The source MIME type of a file
* @param string $to The target MIME type for the file
* @param string $extension The file extension for the target MIME type (e.g. 'png')
* @param string $displayName The human-readable name of the target MIME type (e.g. 'Image (.png)')
*
* @since 31.0.0
*/
public function __construct(
private string $from,
private string $to,
private string $extension,
private string $displayName,
) {
}

public function getFrom(): string {
return $this->from;
}

public function getTo(): string {
return $this->to;
}

public function getExtension(): string {
return $this->extension;
}

public function getDisplayName(): string {
return $this->displayName;
}

/**
* @return array{from: string, to: string, extension: string, displayName: string}
*
* @since 31.0.0
*/
public function jsonSerialize(): array {
return [
'from' => $this->from,
'to' => $this->to,
'extension' => $this->extension,
'displayName' => $this->displayName,
];
}
}
44 changes: 0 additions & 44 deletions lib/public/Files/Conversion/ConversionMimeTuple.php

This file was deleted.

4 changes: 2 additions & 2 deletions lib/public/Files/Conversion/IConversionManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ public function hasProviders(): bool;
/**
* Gets all supported MIME type conversions
*
* @return list<ConversionMimeTuple>
* @return list<ConversionMimeProvider>
*
* @since 31.0.0
*/
public function getMimeTypes(): array;
public function getProviders(): array;

/**
* Convert a file to a given MIME type
Expand Down
2 changes: 1 addition & 1 deletion lib/public/Files/Conversion/IConversionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ interface IConversionProvider {
/**
* Get an array of MIME type tuples this conversion provider supports
*
* @return list<ConversionMimeTuple>
* @return list<ConversionMimeProvider>
*
* @since 31.0.0
*/
Expand Down

0 comments on commit 85bea72

Please sign in to comment.