diff --git a/app/Classes/AttachmentsManager.php b/app/Classes/AttachmentsManager.php index d9cca13..753cc2d 100644 --- a/app/Classes/AttachmentsManager.php +++ b/app/Classes/AttachmentsManager.php @@ -94,23 +94,36 @@ public function add(array $data): string if (!empty($data['contents'])) { // Adding from an image resource of which $data['contents'] is a // representation - $contents = $data['contents']; + $contents = $data['contents']; + $checksum = md5($contents); + $existingRef = $this->findAttachmentByChecksum($checksum); + + if (!empty($existingRef)) { + return $existingRef; + } + + $data['checksum'] = $checksum; unset($data['contents']); - } elseif (!empty($data['url'])) { - $existingRef = $this->findAttachmentByOriginalUrl($data['url']); + } elseif (!empty($data['original_url'])) { + $existingRef = $this->findAttachmentByOriginalUrl($data['original_url']); if (!empty($existingRef)) { return $existingRef; } // Adding from a URL which implies downloading the resource - $contents = Http::throw()->get($data['url'])->body(); + $contents = Http::throw()->get($data['original_url'])->body(); + $checksum = md5($contents); + $existingRef = $this->findAttachmentByChecksum($checksum); - $data['filename'] = basename($data['url']); - $data['original_url'] = $data['url']; + if (!empty($existingRef)) { + return $existingRef; + } - unset($data['url']); + $data['checksum'] = $checksum; + + $data['filename'] = basename($data['original_url']); } $filename = $this->getFormatedFilename($data['filename']); @@ -356,6 +369,20 @@ private function findAttachmentByOriginalUrl(string $url) return null; } + /** + * Find an attachment from file's checksum, if specified + */ + private function findAttachmentByChecksum(string $checksum) + { + foreach ($this->manager->get('files', []) as $ref => $data) { + if (array_key_exists('checksum', $data) && $data['checksum'] === $checksum) { + return $ref; + } + } + + return null; + } + /** * Generate new, random reference for a file */ diff --git a/app/Console/Commands/Bundle/Traits/ReadsBundles.php b/app/Console/Commands/Bundle/Traits/ReadsBundles.php new file mode 100644 index 0000000..d14a700 --- /dev/null +++ b/app/Console/Commands/Bundle/Traits/ReadsBundles.php @@ -0,0 +1,38 @@ +argument('path') ?? '/'; + $comment = sprintf('Validating %s', $path); + + if ($this->option('recursive')) { + $comment .= ' and all sub-bundles'; + } + + $this->line($comment); + $this->output->write('Collecting bundles... '); + + if ($this->option('recursive')) { + $bundles = Bundle::findBundles($this->sourceDisk, $path, true); + } else { + $bundles = [new Bundle($path, $this->sourceDisk)]; + } + + $this->bundles = $bundles; + + $this->info('OK'); + + return $this; + } +} diff --git a/app/Console/Commands/Bundle/Traits/SelectsDisks.php b/app/Console/Commands/Bundle/Traits/SelectsDisks.php new file mode 100644 index 0000000..7bc0fc1 --- /dev/null +++ b/app/Console/Commands/Bundle/Traits/SelectsDisks.php @@ -0,0 +1,30 @@ +option('source-disk') ?? env('CONTENT_DISK'); + + $this->sourceDisk = Storage::disk($sourceDisk); + + $this->line( + sprintf( + 'Using %s as source disk', + $sourceDisk + ) + ); + + return $this; + } +} diff --git a/app/Console/Commands/Bundle/Update.php b/app/Console/Commands/Bundle/Update.php index c6c50dc..be78959 100644 --- a/app/Console/Commands/Bundle/Update.php +++ b/app/Console/Commands/Bundle/Update.php @@ -3,20 +3,19 @@ namespace App\Console\Commands\Bundle; use App\Classes\Bundle; +use App\Console\Commands\Bundle\Traits\ReadsBundles; +use App\Console\Commands\Bundle\Traits\SelectsDisks; use App\Exceptions\BundleUpdaterCannotBeFound; use App\Exceptions\UnableToFindWikidataEntityId; use App\Services\BundleUpdaters\Facades\BundleUpdater; use Illuminate\Console\Command; -use Illuminate\Support\Facades\Storage; + +use function Laravel\Prompts\progress; class Update extends Command { - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = 'bundle:update { path? : Path to a specific bundle to update }'; + use ReadsBundles; + use SelectsDisks; /** * The console command description. @@ -25,48 +24,113 @@ class Update extends Command */ protected $description = 'Update bundles extended metadata'; + protected $notUpdatable = []; + + protected $updatedCount = 0; + + protected $notUpdatedCount = 0; + + public function __construct() + { + $this->signature = 'bundle:update + { --r|recursive : Also validate sub-bundles } + { --source-disk= : Use specified content disk - Defaults to ' . env('CONTENT_DISK') . ' } + { path? : Path to a specific bundle to validate - Default to / } + '; + + parent::__construct(); + } + /** * Execute the console command. */ public function handle() { - $path = $this->argument('path') ?? '/'; - $bundles = Bundle::findBundles(Storage::disk(env('CONTENT_DISK')), $path, true); + $this->selectDisk() + ->selectBundles() + ->update() + ->showReport(); + } - foreach ($bundles as $bundle) { - $this->output->write(sprintf('Updating %s... ', $bundle->getPath())); + /** + * Updates specific bundle + */ + protected function handleBundle(Bundle $bundle, $progress) + { + $progress->hint(sprintf('Updated %s', $bundle->getPath())); - try { - $updater = BundleUpdater::getBundleUpdaterFor($bundle); - } catch (BundleUpdaterCannotBeFound $ex) { - $this->line('Not updatable'); + try { + $updater = BundleUpdater::getBundleUpdaterFor($bundle); + } catch (BundleUpdaterCannotBeFound $ex) { + return; + } - continue; - } + try { + while (!$updater->canUpdateBundle()) { + $specs = $updater->formSpecs(); - try { - while (!$updater->canUpdateBundle()) { - $specs = $updater->formSpecs(); - - foreach ($specs as $key => $func) { - $result = $func(); - $bundle->metadata()->set($key, $result); - $bundle->save(); - } + foreach ($specs as $key => $func) { + $result = $func(); + $bundle->metadata()->set($key, $result); + $bundle->save(); } - } catch (UnableToFindWikidataEntityId $ex) { - $this->comment('Unable to find wikidata entity Id'); - - continue; } + } catch (UnableToFindWikidataEntityId $ex) { + $this->notUpdatable[$bundle->getPath()] = 'Missing Wikidata Entity Id'; - $result = $updater->update(); + return; + } - if ($result) { - $this->info('OK'); - } else { - $this->comment('Update not needed'); - } + $result = $updater->update(); + + if ($result) { + $this->updatedCount++; + } else { + $this->notUpdatedCount++; + } + } + + /** + * Performs update on selected bundles + */ + private function update(): self + { + progress('Updating bundles...', $this->bundles, function (Bundle $bundle, $progress) { + $this->handleBundle($bundle, $progress); + }); + + return $this; + } + + /** + * Show a report of update procedure + */ + private function showReport() + { + $this->line(sprintf('Updated bundles: %d', $this->updatedCount)); + $this->line(sprintf('Update not needed: %d', $this->notUpdatedCount)); + + $this->reportNonUpdatable(); + } + + /** + * Show report of non updatable bundles + */ + private function reportNonUpdatable() + { + $count = count($this->notUpdatable); + + if ($count === 0) { + return; + } + + $this->newLine(); + $this->line(sprintf('Not updatable bundles: %d', $count)); + + foreach ($this->notUpdatable as $bundle => $reason) { + $this->newLine(); + $this->comment($bundle); + $this->line(sprintf(' > %s', $reason)); } } } diff --git a/app/Console/Commands/Bundle/Upgrade.php b/app/Console/Commands/Bundle/Upgrade.php index 1a77aa1..2000713 100644 --- a/app/Console/Commands/Bundle/Upgrade.php +++ b/app/Console/Commands/Bundle/Upgrade.php @@ -40,6 +40,9 @@ public function handle() $bundle->metadata('links')->clear(); $bundle->metadata('links')->save(); + $bundle->metadata('rebrickable')->clear(); + $bundle->metadata('rebrickable')->save(); + $bundle->save(); $this->info('OK'); diff --git a/app/Services/BundleUpdaters/Updaters/LegoUpdater.php b/app/Services/BundleUpdaters/Updaters/LegoUpdater.php index a5295a7..f8cd8d4 100644 --- a/app/Services/BundleUpdaters/Updaters/LegoUpdater.php +++ b/app/Services/BundleUpdaters/Updaters/LegoUpdater.php @@ -72,8 +72,8 @@ public function update() if (!empty($setData['set_img_url'])) { $ref = $this->bundle->attachments(AttachmentsManager::Images)->add([ - 'url' => $setData['set_img_url'], - 'attribution' => '© [Rebrickable](https://rebrickable.com/)', + 'original_url' => $setData['set_img_url'], + 'attribution' => '© [Rebrickable](https://rebrickable.com/)', ]); $this->bundle->metadata()->set('cover', $ref); @@ -86,8 +86,8 @@ public function update() } $this->bundle->attachments(AttachmentsManager::Images)->add([ - 'url' => $minifig['set_img_url'], - 'attribution' => '© [Rebrickable](https://rebrickable.com/)', + 'original_url' => $minifig['set_img_url'], + 'attribution' => '© [Rebrickable](https://rebrickable.com/)', ]); } }