I'm in need of migrating our existing photo-database, and because we have various meta-data per photo (license, photographer etc), I thought I would use the Media module for storage because it allows for custom fields..

So my question is this: Is there any plans of adding a Media destination class? If there isn't, how do I set about creating one. I haven't started at looking at the internals of Media, but I'm thinking that it should be fairly similar to the File destination..

Also, if I created one, could it be bundled with a future version of Migrate – or would that introduce a unwanted dependency on the Media module?

Comments

moshe weitzman’s picture

Category: feature » support
Status: Active » Fixed

Mike just added media module support to migrate_extras - http://drupal.org/cvs?commit=468544

fangel’s picture

Well, in that case, I'll just point out that migrate_extra's Media destination is broken (it doesn't support custom fields, for instance).

I've created a ticket (#1014496: Media destination doesn't support custom fields.) on the migrate_extras tracker..

mikeryan’s picture

Project: Migrate » Migrate Extras
Component: Code » Migrate Extras Features
fangel’s picture

StatusFileSize
new3.29 KB

Okay, I've gotten a slightly hack'ish version running now. It's able to import into Media entries with custom fields..

It's based on the file destination, except $entityType is changed to media, and $bundle is set to a user-supplied parameter.
Then the import method, is copied from the file destination, because I needed to change a few key things:

  • Uses media_load instead of file_load
  • Uses a sequence of file_save and media_save instead of only just file_save

I've asked the Media people (#1018290: API for creating Media instances) if there is a better API for adding media entries instead of the sequence of file_save and media_save..

Notes

  • You CANNOT set the fid in your import - this will cause file_save to try and update the fid, instead of creating a new with the specified fid
  • You MUST have a destination mapping of the field status to the value FILE_STATUS_PERMANENT

-Morten

fangel’s picture

Category: support » task
Status: Fixed » Needs work
fangel’s picture

And now that I've gotten my import into Media to work, I needed a Media field handler too.. So here's a very basic media field handler.

class MigrateMediaFieldHandler extends MigrateFieldHandler {
  public function __construct() {
    $this->registerTypes(array('media'));
  }

  public function prepare(stdClass $entity, array $field_info, array $instance, array $values) {
    $migration = Migration::currentMigration();
    $destination = $migration->getDestination();
    $language = isset($arguments['language']) ?
      $arguments['language'] : $destination->getLanguage();
    
	$delta = 0;
    foreach ($values as $value) {
      $item = array();
      
      if (is_array($value)) {
        $item = $value;
        if (isset($item['data']) && is_array($item['data']))
          $item['data'] = serialize($item['data']);
      } else {
        $item['fid'] = $value;
      }

      $return[$language][$delta] = $item;
      $delta++;
    }

    return isset($return) ? $return : NULL;
  }
}

Notes:

  • ONLY supports fid values. You can't (yet) give it names of media entries and have them looked up.
  • If you supply an array instead of an integer as a value, you can set the fid, title & data properties on the field.

Update, Jan 26th: Added serialization to the data-field if it's not already..

mikeryan’s picture

I've updated the media destination to set the entity type and bundle appropriately, and implemented fields() so the available fields are presented in the migrate UI. Actually saving the fields is still broken - see my comment on #1018290: API for creating Media instances.

fangel’s picture

As I noted above (and as is implemented in my destination handler), if you call media_save after calling file_save and setting type, it works fine. I agree that this is.. annoying... so let's see what the Media guys come back with on the API question..

Also, what do you think of my proposed media-field handler? It should probably do what the file-field does in regards to uploads too, if given a path not a fid - but the fields has more info than fid - it has title + data (the latter is serialized - I've updated the code-sniplet to reflect this)

mikeryan’s picture

OK, I've committed a fix to fields not being saved (added a complete() method to call media_save()).

On the media field handler, let's reuse the filefield implementation as much as possible. Is this workable?

class MigrateMediaFieldHandler extends MigrateFileFieldHandler {
  public function __construct() {
    parent::__construct();
    $this->registerTypes(array('media'));
  }

  public function prepare(stdClass $entity, array $field_info, array $instance, array $values) {
    // Do media-specific stuff here...

    // Do standard filefield stuff
    return parent::prepare($entity, $field_info, $instance, $values);
  }
fangel’s picture

I'll checkout your Media Destination on Monday (or possibly, but unlikely, tomorrow), and see if it works as intended (I have a 5000-file/2gb Media import to test it on)

In regards to the Media-field, then the FieldHandler just needs to return something like this

array($language => array(
  0 => array(
    'fid' => 'file id'; // required
    'title' => 'title for the reference'; // optional
    'data' => 'serialized extra data for the reference'; // optional
  ),
  1 => ...,
);

So perhaps it's better to first call FileFields' prepare()-method to get file-references (if the given value is the path to a file, not a fid) processed (but then what about media_save?), and then change add the optional title and data fields afterwards?

I'm semi-sure that the MediaField probably should increase the file_usage table too, but haven't completely looked into this yet.. It might be handled automatically by the Media modules field-processor.

All in all, I think it's a great idea to build upon FileField for the MediaField, but it needs a bit of thought and work to be perfect..

-Morten

fangel’s picture

Okay, just did a quick test of the updated MigrateDestinationMedia. It works as intended, sets the fields and everything.

One small comment is the order or arguments in MigrateDestinationMedia::options. You will get a "strict"-warning, because the method-interface is different to the one from MigrationDestinationFile which your extend from. So instead, it should be

static public function options($copy_file, $copy_destination, $language, $text_format, $source_migrations = array()) { ... }

This way, the old interface is still applicable, and you wont get a "strict"-warning..

--

A second note, is that I had changed my own destination handler, so it removed a lot of the destination path from the imported image.

Say you have a import that gives you $file->uri = '/Users/fangel/Sites/old_site/modules/..../library/0/0/0001.jpg';. Then I don't want the file's destination to be $copyDestination/Users/fangel/..., I want it to be $copyDestination/0001.jpg..

I can't see why anything but the basename of the uri should be added to the $copyDestination before the file's copied? At least not without a way to turn toggle this behavior.. What's the use-case for this? It means everyone append the full path of the image into their destination path, which results in terribly ugly and long destination paths..

mikeryan’s picture

I've fixed the options order, thanks.

basename won't work right if you have a situation like

http://example.com/images/2010/12/my_image.jpg
http://example.com/images/2011/01/my_image.jpg

It's not at all unusual in practice for basenames to be duplicated within the file hierarchy.

It seems to me we had something to trim a specified prefix from the URI - it must've been in a particular project, it would be worth generalizing in MigrateDestinationFile.

mikeryan’s picture

Oh, another (perhaps even better) reason to preserve the full path is to preserve the URL of the files, so when you move your old domain to the new server links still work as-is.

fangel’s picture

Except, it's the full absolute path it gets imported as, not relative to the webroot.. So I e.g. the file /Users/fangel/Sites/xxx/.../001.jpg sits at /~fangel/xxx/.../001.jpg, relative to my webroot - which you wouldn't be able to tell programatically..

So either use basename and inform people that conflicts are BAD, and they should set their $copyDestination to i.e. public://migration-name, not leave it at public:// because that could cause conflicts
OR use some system to allow string-replacement afterwards (ie replace /Users/fangel/Sites with /~fangel)

But in anyway, this deserves a seperate issue, as it's related to the File destination in the Media module..

FrequenceBanane’s picture

so, how is it going with MigrateMediaFieldHandler ?

fangel’s picture

Hi,

I don't know where Mike is with having a "official" Media field handler in Migrate Extra, as I haven't been looking at his progress for a while.

If you just need something quick-and-dirty, use the one I posted above (comment #6). I've been using that to import ~6k Media entities, and haven't had a issue with it yet.

-Morten

mfb’s picture

subscribe.. will be testing out migrate_extras + media soon.

mfb’s picture

A couple things I noticed so far: 1) re: MigrateDestinationMedia, media module no longer has media entities, it just has file entities, and 2) re: MigrateMediaFieldHandler, media fields are pretty similar to file fields, except that a media field contains a file object, while a file field contains a file array. So it won't be quite as simple as returning MigrateFileFieldHandler::prepare() as suggested in #9 Nevermind, media fields have a file object when loaded but not when saved.

mfb’s picture

Status: Needs work » Needs review
StatusFileSize
new8.9 KB

Here's a first stab at MigrateMediaFieldHandler. I couldn't reuse MigrateFileFieldHandler::buildFileArray() as is, but more code could be reused if buildFileArray() were broken up into pieces.

BTW, I think file_fast in MigrateFileFieldHandler is broken: there are a couple instances of $fids[$source] which doesn't make sense as $source is an object.

mikeryan’s picture

Component: Migrate Extras Features » Media
Status: Needs review » Fixed

I committed this from Amtrak the other day, and now realize the connection dropped before my reply went through... Testing was minimal, if any problems are found please open a new issue. Thanks!

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

jazzslider’s picture

Status: Closed (fixed) » Active

Hello!

Based on my experimentation with this functionality this afternoon, it looks as though the patch above was designed back when the Media module exposed its own media entities instead of just making the D7 core file entity fieldable (cf. #1086958: Switch from Media Entity to File Entity for a very extensive discussion of the issue; it was also briefly referenced in this thread at #18). My evidence is that migrate_extras/media.inc still attempts to make use of the media_save() function, which no longer exists.

Unfortunately I'm a bit new to the structure of all these modules, so I'm not entirely sure what would need to change here to ensure compatibility with Media's new approach …that is, although I'm sure that media_save() can no longer be used, I'm not sure that file_save() will be sufficient.

Does anyone have any ideas on this?

Thanks!
Adam

mikeryan’s picture

For some reason, I thought I had committed a patch dealing with the changes, but apparently not... OK, I will need to review this when I have time.

Thanks.

mfb’s picture

yes MigrateDestinationMedia is broken as mentioned in #18. The patch above only dealt with MigrateMediaFieldHandler

Fidelix’s picture

Subscribing...

jerdavis’s picture

Status: Active » Needs work

Anyone put in any further work on this or tested the patch from #19?

alanburke’s picture

Subscribe

mikeryan’s picture

Assigned: Unassigned » mikeryan

I should be fixing this up within a couple of days.

jerdavis’s picture

Status: Needs work » Needs review
StatusFileSize
new15.73 KB
new7.06 KB

I started looking at this today and ended up pretty much starting from scratch. We needed a Media destination for migration, and that was not part of the previous update. I'm attaching two patches, one that replaces all of the media.inc functionality with just a working MigrateDestinationMedia plugin and a second which adds the working destination to the existing media.inc. I'm assuming the field handler will need refactoring as well, so I'll let you decide which to apply.

mikeryan’s picture

Status: Needs review » Needs work

Looked over your patch, and the condition of the field handler, and I think the Migrate module can support most of the need with just a little generalization: #1240928: META: Refactoring of file destination/field handlers.

mikeryan’s picture

Well, as mentioned at #1240928: META: Refactoring of file destination/field handlers, I've made a couple of tweaks to the Migrate file handler support that were helpful, and I've just now pushed a new media.inc that should be semi-useful for the moment - at least it works for image files, although no luck with youtube references. I've concluded that the Migrate file handling needs some refactoring, but I'd like to get 2.2 released before tackling that.

jerdavis’s picture

Hi MIke,

Any hints on what you're seeing with Videos? Looks like we're running into the same thing. I'll check out your changes and see what I can do.

Jeremiah

jerdavis’s picture

StatusFileSize
new5.16 KB

Well, I've hacked things up a bit to get it to work here for video. I'm attaching a patch.

Key things in this patch

1) Your updates to plugins/destinations/file.inc in Migrate module didn't include some key code from my previous patch that allow for migration of additional meta data into the file entity. Here's the relevant code from my modified prepare() method:

    $migration = Migration::currentMigration();
    // Call any general entity handlers (in particular, the builtin field handler)
    migrate_handler_invoke_all('Entity', 'prepare', $entity, $source_row);
    // Then call any entity-specific handlers
    migrate_handler_invoke_all($this->entityType, 'prepare', $entity, $source_row);
    // Then call any prepare handler for this specific Migration.
    if (method_exists($migration, 'prepare')) {
      $migration->prepare($entity, $source_row);
    }

Second, to get videos importing I found I need to call API functions from file_entity and media modules in order to get a more well defined file object. Here's the relevant bit from my modified import() method:

    if ($this->copyFile) {
    ... 
   }
    else {
      // For external sources where we aren't copying the file build a more complete object.
      $file->uri = media_parse_to_uri($file->uri);
      $file = (object) array_merge((array) file_uri_to_object($file->uri), (array) $file);
    }

Feels dirty, but it works in my limited testing. I'm sure there's a cleaner way to handle this, perhaps your re-write of the file handling will address it. Let me know if you have some thoughts on this.

mikeryan’s picture

Yes, that's the issue, media needs to do stuff where the file handler has firm control. I'm not going to spend time (of which I have none:-) trying to work around things as they are - I'm looking ahead to refactoring the file handlers, particularly with media in mind.

jerdavis’s picture

Hi MIke,

Sounds good - I'll keep posting updates here if we run into issues in our own migrations in case someone else needs a stop-gap solution.

Jeremiah

wusel’s picture

I use:
Drupal 7.10
Migrate 7.x-2.x-dev (2011-Dec-16)
Migrate Extras 7.x-2.x-dev (2011-Oct-26)

I get the error-message:

Strict warning: Declaration of MigrateDestinationMedia::options() should be compatible with that of MigrateDestinationFile::options() in _registry_check_code() (Line 3004 of G:\xampp\htdocs\abt\includes\bootstrap.inc).

I hope, this error belongs to this issue.

Please help.

Thanks for this very excellent module!

pjcdawkins’s picture

wusel (#36):

I also get that error, it just seems to be because MigrateDestinationFile hasn't been written with easy extension in mind. It isn't important - just lower the error reporting level in your settings.php or in admin/config/development/logging.

wusel’s picture

Category: task » bug

The error has not gone...

mikeryan’s picture

Category: bug » task
Status: Needs work » Fixed

Please open distinct issues in the queue for distinct problems you see, let's not keep tacking everything media-related on to this issue.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.