-
-
Notifications
You must be signed in to change notification settings - Fork 309
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[stacked 5] Add initial steps as Pipes (#2368)
- Loading branch information
Showing
9 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
namespace App\Actions\Photo\Pipes\Init; | ||
|
||
use App\Contracts\PhotoCreate\InitPipe; | ||
use App\DTO\PhotoCreate\InitDTO; | ||
use App\Exceptions\MediaFileOperationException; | ||
use App\Exceptions\MediaFileUnsupportedException; | ||
|
||
/** | ||
* Assert whether we support said file. | ||
*/ | ||
class AssertSupportedMedia implements InitPipe | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
* | ||
* @throws MediaFileUnsupportedException | ||
* @throws MediaFileOperationException | ||
*/ | ||
public function handle(InitDTO $state, \Closure $next): InitDTO | ||
{ | ||
$state->sourceFile->assertIsSupportedMediaOrAcceptedRaw(); | ||
|
||
return $next($state); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
namespace App\Actions\Photo\Pipes\Init; | ||
|
||
use App\Contracts\PhotoCreate\InitPipe; | ||
use App\DTO\PhotoCreate\InitDTO; | ||
|
||
/** | ||
* Set fileLastModifiedTime if null. | ||
*/ | ||
class FetchLastModifiedTime implements InitPipe | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function handle(InitDTO $state, \Closure $next): InitDTO | ||
{ | ||
if ($state->fileLastModifiedTime === null) { | ||
$state->fileLastModifiedTime ??= $state->sourceFile->lastModified(); | ||
} | ||
|
||
return $next($state); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
namespace App\Actions\Photo\Pipes\Init; | ||
|
||
use App\Contracts\PhotoCreate\InitPipe; | ||
use App\DTO\PhotoCreate\InitDTO; | ||
use App\Image\StreamStat; | ||
use App\Models\Photo; | ||
|
||
/** | ||
* Look for duplicates of the file in the database. | ||
*/ | ||
class FindDuplicate implements InitPipe | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function handle(InitDTO $state, \Closure $next): InitDTO | ||
{ | ||
$checksum = StreamStat::createFromLocalFile($state->sourceFile)->checksum; | ||
|
||
$state->duplicate = Photo::query() | ||
->where('checksum', '=', $checksum) | ||
->orWhere('original_checksum', '=', $checksum) | ||
->orWhere('live_photo_checksum', '=', $checksum) | ||
->first(); | ||
|
||
return $next($state); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
|
||
namespace App\Actions\Photo\Pipes\Init; | ||
|
||
use App\Contracts\PhotoCreate\InitPipe; | ||
use App\DTO\PhotoCreate\InitDTO; | ||
use App\Exceptions\Internal\IllegalOrderOfOperationException; | ||
use App\Exceptions\Internal\LycheeAssertionError; | ||
use App\Image\Files\BaseMediaFile; | ||
use App\Models\Photo; | ||
|
||
/** | ||
* Try to link live photo components together. | ||
*/ | ||
class FindLivePartner implements InitPipe | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function handle(InitDTO $state, \Closure $next): InitDTO | ||
{ | ||
try { | ||
// find a potential partner which has the same content id | ||
if ($state->exifInfo->livePhotoContentID !== null) { | ||
$state->livePartner = Photo::query() | ||
->where('live_photo_content_id', '=', $state->exifInfo->livePhotoContentID) | ||
->where('album_id', '=', $state->album?->id) | ||
->whereNull('live_photo_short_path')->first(); | ||
} | ||
|
||
// if a potential partner has been found, ensure that it is of a | ||
// different kind then the uploaded media. | ||
if ( | ||
$state->livePartner !== null && !( | ||
BaseMediaFile::isSupportedImageMimeType($state->exifInfo->type) && $state->livePartner->isVideo() || | ||
BaseMediaFile::isSupportedVideoMimeType($state->exifInfo->type) && $state->livePartner->isPhoto() | ||
) | ||
) { | ||
$state->livePartner = null; | ||
} | ||
|
||
return $next($state); | ||
} catch (IllegalOrderOfOperationException $e) { | ||
throw LycheeAssertionError::createFromUnexpectedException($e); | ||
} | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
<?php | ||
|
||
namespace App\Actions\Photo\Pipes\Init; | ||
|
||
use App\Contracts\PhotoCreate\InitPipe; | ||
use App\DTO\PhotoCreate\InitDTO; | ||
use App\Exceptions\InvalidPropertyException; | ||
use App\Models\Album; | ||
use App\SmartAlbums\BaseSmartAlbum; | ||
use App\SmartAlbums\StarredAlbum; | ||
|
||
/** | ||
* Init album. | ||
*/ | ||
class InitParentAlbum implements InitPipe | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
* | ||
* @throws InvalidPropertyException | ||
*/ | ||
public function handle(InitDTO $state, \Closure $next): InitDTO | ||
{ | ||
if ($state->album === null || $state->album instanceof Album) { | ||
return $next($state); | ||
} | ||
|
||
if ($state->album instanceof BaseSmartAlbum) { | ||
if ($state->album instanceof StarredAlbum) { | ||
$state->is_starred = true; | ||
} | ||
|
||
$state->album = null; | ||
|
||
return $next($state); | ||
} | ||
|
||
throw new InvalidPropertyException('The given parent album does not support uploading'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<?php | ||
|
||
namespace App\Actions\Photo\Pipes\Init; | ||
|
||
use App\Contracts\PhotoCreate\InitPipe; | ||
use App\DTO\PhotoCreate\InitDTO; | ||
use App\Exceptions\InvalidPropertyException; | ||
use App\Metadata\Extractor; | ||
|
||
/** | ||
* Load metadata from the file. | ||
*/ | ||
class LoadFileMetadata implements InitPipe | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
* | ||
* @throws InvalidPropertyException | ||
*/ | ||
public function handle(InitDTO $state, \Closure $next): InitDTO | ||
{ | ||
$state->exifInfo = Extractor::createFromFile($state->sourceFile, $state->fileLastModifiedTime); | ||
|
||
// Use basename of file if IPTC title missing | ||
if ( | ||
$state->exifInfo->title === null || | ||
$state->exifInfo->title === '' | ||
) { | ||
$state->exifInfo->title = substr($state->sourceFile->getOriginalBasename(), 0, 98); | ||
} | ||
|
||
return $next($state); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
namespace App\Contracts\PhotoCreate; | ||
|
||
use App\DTO\PhotoCreate\InitDTO; | ||
|
||
/** | ||
* Basic definition of a Photo creation pipe. | ||
* | ||
* This allows to clarify which steps are applied in which order. | ||
*/ | ||
interface InitPipe | ||
{ | ||
/** | ||
* @param InitDTO $state | ||
* @param \Closure(InitDTO $state): InitDTO $next | ||
* | ||
* @return InitDTO | ||
*/ | ||
public function handle(InitDTO $state, \Closure $next): InitDTO; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php | ||
|
||
namespace App\DTO\PhotoCreate; | ||
|
||
use App\Contracts\Models\AbstractAlbum; | ||
use App\DTO\ImportMode; | ||
use App\DTO\ImportParam; | ||
use App\Image\Files\NativeLocalFile; | ||
use App\Metadata\Extractor; | ||
use App\Models\Photo; | ||
|
||
class InitDTO | ||
{ | ||
// Import mode. | ||
public readonly ImportMode $importMode; | ||
|
||
// Indicates the intended owner of the image. | ||
public readonly int $intendedOwnerId; | ||
|
||
// Indicates whether the new photo shall be starred. | ||
public bool $is_starred = false; | ||
|
||
// The extracted EXIF information (populated during init phase). | ||
public ?Extractor $exifInfo; | ||
|
||
// The intended parent album | ||
public ?AbstractAlbum $album = null; | ||
|
||
// The original photo source file that is imported. | ||
public NativeLocalFile $sourceFile; | ||
|
||
// During initial steps if a duplicate is found, it will be placed here. | ||
public Photo|null $duplicate = null; | ||
|
||
// During initial steps if liveParner is found, it will be placed here. | ||
public Photo|null $livePartner = null; | ||
|
||
// Optional last modified data if known. | ||
public int|null $fileLastModifiedTime = null; | ||
|
||
public function __construct( | ||
ImportParam $parameters, | ||
NativeLocalFile $sourceFile, | ||
AbstractAlbum|null $album, | ||
int|null $fileLastModifiedTime = null | ||
) { | ||
$this->sourceFile = $sourceFile; | ||
$this->importMode = $parameters->importMode; | ||
$this->intendedOwnerId = $parameters->intendedOwnerId; | ||
$this->is_starred = $parameters->is_starred; | ||
$this->exifInfo = $parameters->exifInfo; | ||
$this->album = $album; | ||
$this->fileLastModifiedTime = $fileLastModifiedTime; | ||
} | ||
} |