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/)',
]);
}
}