thomasvantuycom / craft-cloudinary

Cloudinary integration for Craft CMS
MIT License
5 stars 3 forks source link

Check if asset exists in cloudinary before upload? #4

Closed robinbeatty closed 9 months ago

robinbeatty commented 11 months ago

Hi Thomas, on before upload to cloudinary how easy would it be to implement a quick query to see if the asset already exists (matched by filename/bytes) and if so, insert the existing asset into the index rather than a duplicate? (to match the behaviour of craft's native assets fs)? This would be ideal for our use case.

thomasvantuycom commented 11 months ago

Hi Robin,

I'm not quite sure what you mean. When I re-upload the identical asset, Craft identifies it and suggests replacing the current one, both for the Cloudinary filesystem and the native local filesystem. Are you referring to a different scenario?

https://github.com/thomasvantuycom/craft-cloudinary/assets/107400578/e677eb94-a539-4f93-b7f4-daebac165ed7

robinbeatty commented 11 months ago

Yes and that's great, though for our use case we need to be able to use the Cloudinary Media Browser widget UI on our frontend to upload an image, and for that to create a relationship in the craft db to the equivalent asset. Because the asset that gets uploaded to Cloudinary isn't yet indexed by craft, my solution is to send the new cloudinary asset url (which we can have returned from the widget) into Craft as a GraphQL save_[cloudinaryVolume]_Asset mutation and return the Craft asset id, which then gets saved to the entry's asset field (via GraphQL). Because Craft doesn't yet know about the new asset in Cloudinary we end up with two copies of the asset in Cloudinary and, next time it's indexed, two copies of the asset in the Craft db too.

If there was a way to query for the existence of the Cloudinary asset (as well as the craft asset which you demonstrated) then I think that would solve the problem. Happy to fork the plugin if you think this is probably an esoteric use case (maybe you could point me where I could insert the query logic?) but I imagine the most common use case is users wanting the cloudinary assets and the craft assets to stay in sync in both directions.

Another solution could be to get Craft to index the new asset and then query for it, but I don't think it's currently possible to just index specific assets programmatically, unless you know otherwise? Obviously indexing the whole volume would be too costly.

thomasvantuycom commented 11 months ago

I'll explore webhooks as they are likely more appropriate for implementing this type of two-way synchronization.

thomasvantuycom commented 11 months ago

Just a quick update: I haven't managed to make it work yet. I'm curious about your choice to use the Cloudinary upload widget. Wouldn't the issue be resolved by allowing Craft to manage the frontend user's uploads?

robinbeatty commented 11 months ago

The reason is because we need the full Cloudinary media browser experience – navigate folders, search, AI generated tags on upload etc etc. It would be a huge project to roll our own on top of Craft's GraphQL api. But we do need the assets uploaded like this to be immediately available as Craft assets. Another solution could be to be able to ask Craft to index one Cloudinary asset by filename.. do you know if that is possible?

thomasvantuycom commented 9 months ago

Alright, so it is possible to index a single asset. Here's how:

$volume = Craft::$app->getVolumes()->getVolumeByHandle('cloudinary');
$assetIndexer = Craft::$app->getAssetIndexer();
$session = $assetIndexer->createIndexingSession([$volume], false);
$assetIndexer->indexFile($volume, 'path/to/test.jpg', $session->id);
$assetIndexer->stopIndexingSession($session);

Here's the suggested workflow: first, upload the asset to Cloudinary using the upload widget. Next, index the asset in Craft by utilizing the file path from the returned data, which includes the folder and public ID. Finally, query Craft for the asset's ID.

robinbeatty commented 9 months ago

Thanks for the tip! That looks like a great solution. Will create a controller to wrap this all together. Many thanks!