Use Smugmug Photo Service for Media Backend

  • Aaron
  • January 14, 2024
  • 10min
Smugmug integration with media entity
For personal websites, media management, storage and performance can be a concern, for phase one of the project i turned to Smugmug and here's how i did it.

When i made my website, considering budget i chose the cheapest tier of VPS by godaddy, which means it’s not going to be lightening fast, and obviously it doesn’t come with CDN or DAM,  however, i do use Smugmug for my personal photography storage, since it let’s you upload images or videos in original quality

How do i integrate Smugmug as a DAM for Drupal? I looked around but i don’t see any solutions, the only module Media entity SmugMug is under maintenance and from the look of it, it’s not even close to be released anytime soon.

What i need is fair simple, it only needs to accommodate this:

  • I uploade images to Smugmug
  • When i need to embed images in my content, i browse to the image on smugmug and it creates a media entity for me automatically
  • The media entity renders using the smugmug src URL
  • Boom! Faster loading and save storage space

So for the first version, i need to take the following steps:

  1. Create a new media entity for smugmug (or just use the default image) doesn’t make a difference
  2. Add the external URL field to the entity along with controller fields (use modal, use figure…etc)
  3. Create a new block type: media gallery with setting fields: Grid Info, Use Masonry…
  4. Render Media entity to prioritize Smugmug URL field (if this field presents, ignore the file field

And soon i realized an issue, which i have run into while integrating Acquia’s Widen DAM in the past, which is:

The Thumbnail field doesn’t can’t fetch over external URL field, and it’s showing me something like this:

Broken Media Thumbnail in the Media Tab


 

And overwriting this thumbnail field isn’t easy, there’s an ongoing debate over how to open it’s API for overwriting the field.  Reading over these comments also made me realize the downside of open source projects which is everyone has an opinion and every scenario needs to be accommodated

So i have to come up with my fix:

While saving media entity → Hijack the process → Update Image field (which is being used by drupal to render thumbnail) → Call to generate thumbnail queue

/**

* Implements hook_ENTITY_TYPE_presave().

*

* Override the preSave function from \Drupal\media\Entity\Media

* We need to retrieve the thumbnail information from the DAM (Smugmug)

* Once we validate the thumbnail is valid, we save it to the file system

* We reference this file from our thumb field.

*/

function aecka_media_media_presave(MediaInterface $entity) {

$source = $entity->getSource();

if ($source->getPluginId() !== 'image') {

  return;

}



/**

 * Check if the External Image URL field is empty

 * If it's empty, the media defaults to using image field which yields

 * the same logic process from image media entity

 */



$external_link = $entity->get('field_external_image_url');

if ($external_link->isEmpty()) {

  \Drupal::logger('aecka_media')->info('skip saving media @uri. the external url is empty', ['@uri' => $entity->getName()]);

  return;

}



// Getting all the basic field information

$media_url = $external_link->getValue()[0]['uri'];

$media_title = $external_link->getValue()[0]['title'];

$image_field_name = 'field_media_image';

$img_info = pathinfo($media_url);

$filename = strtolower(str_replace(' ', '_',$media_title)."_"."{$img_info['filename']}.{$img_info['extension']}");



// Check if the media image field has already been updated

if($thumbnail_field = $entity->get($image_field_name)->getValue()){

  $existing_file = File::load($thumbnail_field[0]['target_id']);

  if ($filename == $existing_file->getFilename()){

    // same file, abort action to prevent constant uploading files to server

    \Drupal::logger('aecka_media')->info('skip saving media thumbnail from remote @uri. the thumbnail already exist', ['@uri' => $media_url]);

    return;

  }

};



// We retrieve the file and store in our file system

$file = aecka_media_help_retrieve_file($media_url,$filename, 'public://images/pictures/');

if ($file && $file->fid) {

  $entity->set($image_field_name, [

    'target_id' => $file->id(),

    'alt' => $media_title,

    'title' => $media_title,

  ]);

}

else {

  \Drupal::logger('aecka_media')->warning('Unable to save media from remote @uri.', ['@uri' => $media_url]);

  return;

}



/** @var \Drupal\media\Entity\Media $entity */

$entity->updateQueuedThumbnail();

}

Finally, the media thumbnails are showing now

Media thumbnail is now fetched from smugmug

Render the image is quite easy:

Check if image has External URL field defined → Use Smugmug URL as source → Fetch Media settings and generate either a figure object or IMG object

 

A couple other hiccups:

  1. Lightbox doesn’t have an easy way to toggle fullscreen through data object, or at least i didn’t find one, i have to write a data module to invoke lightbox api to popup the modal box
  2. Masonry with bootstrap grid system doesn’t work very well for me, could be my settings could be the native support, i ended up using Isotope-layout, here’s how i load the Isotope through ES6:

    const Isotope = require('isotope-layout');
    const jQueryBridget = require('jquery-bridget');
    jQueryBridget( 'isotope', Isotope, $ );