File "CleanCommand.php"
Full Path: /home/clickysoft/public_html/jmapi5.clickysoft.net/vendor/spatie/laravel-medialibrary/src/MediaCollections/Commands/CleanCommand.php
File size: 8.28 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace Spatie\MediaLibrary\MediaCollections\Commands;
use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
use Illuminate\Contracts\Filesystem\Factory;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Str;
use Spatie\MediaLibrary\Conversions\Conversion;
use Spatie\MediaLibrary\Conversions\ConversionCollection;
use Spatie\MediaLibrary\Conversions\FileManipulator;
use Spatie\MediaLibrary\MediaCollections\Exceptions\DiskDoesNotExist;
use Spatie\MediaLibrary\MediaCollections\MediaRepository;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\MediaLibrary\ResponsiveImages\RegisteredResponsiveImages;
use Spatie\MediaLibrary\Support\PathGenerator\PathGeneratorFactory;
class CleanCommand extends Command
{
use ConfirmableTrait;
protected $signature = 'media-library:clean {modelType?} {collectionName?} {disk?}
{--dry-run : List files that will be removed without removing them},
{--force : Force the operation to run when in production},
{--rate-limit= : Limit the number of requests per second},
{--delete-orphaned : Delete orphaned media items},
{--skip-conversions : Do not remove deprecated conversions}';
protected $description = 'Clean deprecated conversions and files without related model.';
protected MediaRepository $mediaRepository;
protected FileManipulator $fileManipulator;
protected Factory $fileSystem;
protected bool $isDryRun = false;
protected int $rateLimit = 0;
public function handle(
MediaRepository $mediaRepository,
FileManipulator $fileManipulator,
Factory $fileSystem,
) {
$this->mediaRepository = $mediaRepository;
$this->fileManipulator = $fileManipulator;
$this->fileSystem = $fileSystem;
if (! $this->confirmToProceed()) {
return;
}
$this->isDryRun = $this->option('dry-run');
$this->rateLimit = (int) $this->option('rate-limit');
if ($this->option('delete-orphaned')) {
$this->deleteOrphanedMediaItems();
}
if (! $this->option('skip-conversions')) {
$this->deleteFilesGeneratedForDeprecatedConversions();
}
$this->deleteOrphanedDirectories();
$this->info('All done!');
}
/** @return Collection<int, Media> */
public function getMediaItems(): Collection
{
$modelType = $this->argument('modelType');
$collectionName = $this->argument('collectionName');
if (is_string($modelType) && is_string($collectionName)) {
return $this->mediaRepository->getByModelTypeAndCollectionName(
$modelType,
$collectionName
);
}
if (is_string($modelType)) {
return $this->mediaRepository->getByModelType($modelType);
}
if (is_string($collectionName)) {
return $this->mediaRepository->getByCollectionName($collectionName);
}
return $this->mediaRepository->all();
}
protected function deleteOrphanedMediaItems(): void
{
$this->getOrphanedMediaItems()->each(function (Media $media): void {
if ($this->isDryRun) {
$this->info("Orphaned Media[id={$media->id}] found");
return;
}
$media->delete();
if ($this->rateLimit) {
usleep((1 / $this->rateLimit) * 1_000_000);
}
$this->info("Orphaned Media[id={$media->id}] has been removed");
});
}
/** @return Collection<int, Media> */
protected function getOrphanedMediaItems(): Collection
{
$collectionName = $this->argument('collectionName');
if (is_string($collectionName)) {
return $this->mediaRepository->getOrphansByCollectionName($collectionName);
}
return $this->mediaRepository->getOrphans();
}
protected function deleteFilesGeneratedForDeprecatedConversions(): void
{
$this->getMediaItems()->each(function (Media $media) {
$this->deleteConversionFilesForDeprecatedConversions($media);
if ($media->responsive_images) {
$this->deleteDeprecatedResponsiveImages($media);
}
if ($this->rateLimit) {
usleep((1 / $this->rateLimit) * 1_000_000 * 2);
}
});
}
protected function deleteConversionFilesForDeprecatedConversions(Media $media): void
{
$conversionFilePaths = ConversionCollection::createForMedia($media)->getConversionsFiles($media->collection_name);
$conversionPath = PathGeneratorFactory::create($media)->getPathForConversions($media);
$currentFilePaths = $this->fileSystem->disk($media->disk)->files($conversionPath);
collect($currentFilePaths)
->reject(fn (string $currentFilePath) => $conversionFilePaths->contains(basename($currentFilePath)))
->reject(fn (string $currentFilePath) => $media->file_name === basename($currentFilePath))
->each(function (string $currentFilePath) use ($media) {
if (! $this->isDryRun) {
$this->fileSystem->disk($media->disk)->delete($currentFilePath);
$this->markConversionAsRemoved($media, $currentFilePath);
}
$this->info("Deprecated conversion file `{$currentFilePath}` ".($this->isDryRun ? 'found' : 'has been removed'));
});
}
protected function deleteDeprecatedResponsiveImages(Media $media): void
{
$conversionNamesWithResponsiveImages = ConversionCollection::createForMedia($media)
->filter(fn (Conversion $conversion) => $conversion->shouldGenerateResponsiveImages())
->map(fn (Conversion $conversion) => $conversion->getName())
->push('media_library_original');
/** @var array<int, string> $responsiveImagesGeneratedFor */
$responsiveImagesGeneratedFor = array_keys($media->responsive_images);
collect($responsiveImagesGeneratedFor)
->map(fn (string $generatedFor) => $media->responsiveImages($generatedFor))
->reject(fn (RegisteredResponsiveImages $responsiveImages) => $conversionNamesWithResponsiveImages->contains($responsiveImages->generatedFor))
->each(function (RegisteredResponsiveImages $responsiveImages) {
if (! $this->isDryRun) {
$responsiveImages->delete();
}
});
}
protected function deleteOrphanedDirectories(): void
{
$diskName = $this->argument('disk') ?: config('media-library.disk_name');
if (is_null(config("filesystems.disks.{$diskName}"))) {
throw DiskDoesNotExist::create($diskName);
}
$mediaClass = config('media-library.media_model');
$mediaInstance = new $mediaClass();
$keyName = $mediaInstance->getKeyName();
$mediaIds = collect($this->mediaRepository->all()->pluck($keyName)->toArray());
/** @var array<int, string> */
$directories = $this->fileSystem->disk($diskName)->directories();
collect($directories)
->filter(fn (string $directory) => is_numeric($directory))
->reject(fn (string $directory) => $mediaIds->contains((int) $directory))
->each(function (string $directory) use ($diskName) {
if (! $this->isDryRun) {
$this->fileSystem->disk($diskName)->deleteDirectory($directory);
}
if ($this->rateLimit) {
usleep((1 / $this->rateLimit) * 1_000_000);
}
$this->info("Orphaned media directory `{$directory}` ".($this->isDryRun ? 'found' : 'has been removed'));
});
}
protected function markConversionAsRemoved(Media $media, string $conversionPath): void
{
$conversionFile = pathinfo($conversionPath, PATHINFO_FILENAME);
$generatedConversionName = null;
$media->getGeneratedConversions()
->filter(
fn (bool $isGenerated, string $generatedConversionName) => Str::contains($conversionFile, $generatedConversionName)
)
->each(
fn (bool $isGenerated, string $conversionName) => $media->markAsConversionNotGenerated($conversionName)
);
$media->save();
}
}