How to Build Media Applications With Vidispine Development Toolkit (pt3)

By Filip - September 26, 2018 (Last updated: October 11, 2018)

This is the third part of the Vidispine Development Toolkit tutorial, where you’ll learn yo use a grid component to show repository items, as well as how to preview different types of items in the system. When finished you should be able to import and play/show videos and images.

Introduction

Welcome to the third part of the Vidispine Development Toolkit tutorial. In this part, we’ll add a grid with repository items using the VdtGrid component, and show how you can preview items such as images, and video together with their metadata using VdtShape, VdtMetadata, VdtImagePreview, and VdtVideoPlayer components.

VDT Tutorial - part 3 end result

This blog post

  • Creating a wrapper Vue component to host the other Vue components
  • Creating the Items and Preview Vue components
  • Use VdtGrid to show existing items in the Vidispine repository
  • Connecting the Items and Preview Vue components
  • Adding the VdtShape, VdtMetadata, VdtImagePreview, VdtVideoPlayer VDT components

Getting started

All the Vue components that we are going to create should be located in [my-project]/src/components. Default welcome page that is visible when we have just set up the Vidispine Development Toolkit is named VidispineVersion.vue and can be used as a learning reference.

All code can be found in the companion Github repo howto-build-vdt-applications. If you haven’t set up your system yet, head over to part 1 of the tutorial, and then part 2 to learn how to build the import component.

Wrapper, Items, and Preview

Start by creating a new Vue component file within the components folder, and call it Front.vue. Create two additional files, at the same level as the wrapper file, and name them Items.vue and Preview.vue. Import the import (from part 2 of the tutorial), items and preview Vue components to your wrapper component, then declare them as Vue components.

In Front.vue:

<template>
  <div class="media-supply-chain-wrapper">
    <Import/>
    <Items/>
    <Preview/>
  </div>
</template>

<script>
import Import from './Import.vue';
import Items from './Items.vue';
import Preview from './Preview.vue';

export default {
  name: 'MediaSupplyChain',
  components: {
    Import,
    Items,
    Preview,
  },
};
</script>

<style lang="scss" scoped>
.media-supply-chain-wrapper {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  grid-column-gap: 1em; height: 100vh;
  font-size: 1rem;
}
</style>

Adding VdtGrid

Head into your Items.vue file and start by importing and declaring the grid component and the itemApi function. Inside a mounted() function, call the itemApi function to retrieve the items previously imported to the application.

In Items.vue:

<template>
  <div class="items-list">
    <h1>Items</h1>
    <VdtGrid
      :assets="assets"/>
  </div>
</template>

<script>
// eslint-disable-next-line import/no-extraneous-dependencies 
import { VdtGrid } from '@vidispine/vdt-vue-components/es';
import itemApi from '../api/item.api';

export default {
  name: 'Items',
  components: {
    VdtGrid,
  },
  data() {
    return {
      assets: [],
    };
  },
  mounted() {
    return itemApi.getItems('', 1, 100).then((response) => {
      if (response.items.length) {
        this.assets = response.items;
      }
    });
  },
};
</script>

<style lang="scss" scoped>
.items-list {
  height: 100vh;
  overflow: auto;
}
</style>

The VdtGrid component has functions bound to it, for example, the assetClick emit, which will send the item ID of the clicked grid item up to its parent. The id of the clicked item is fairly useless in the list of items but is key in displaying singular items and its content. Lets forward the result of the selected emit another step so that the preview Vue component can access it. The string within single quotations is the name of the function passed on and the $event is the object sent with it.

In Items.vue <template>:

<VdtGrid
  :assets="assets"
  @assetClick="$emit('selected', $event)"/>

Connecting Items and Preview

The function is now forwarded to the parent, in this case, the wrapper created earlier. Set up the same listener in the wrapper document and now bind it to a function, in the example below it is named select. The item ID is extracted from the object and passed to the itemId data object, which is then passed on to the preview Vue component.

In Front.vue:

<template>
  <div class="media-supply-chain-wrapper">
    <Import/>
    <Items @selected="select($event)"/>
    <Preview :item-id="itemId"/>
  </div>
</template>

<script>
export default {
  name: 'MediaSupplyChain',
  components: {
    Import,
    Items,
    Preview,
  },
  data() {
    return {
      itemId: '',
    };
  },
  methods: {
    select(asset) {
      this.itemId = asset.metadata.id;
    },
  },
};
</script>

Building the preview document

Head over to the Preview.vue file and import the necessary components and set up the prop sent by the parent wrapper component earlier.

In Preview.vue:

<script>
// eslint-disable-next-line import/no-extraneous-dependencies  
import { VdtShape, VdtMetadata, VdtImagePreview, VdtVideoPlayer } from '@vidispine/vdt-vue-components/es';

export default {
  name: 'Preview',
  components: {
    VdtShape,
    VdtMetadata,
    VdtImagePreview,
    VdtVideoPlayer,
  },
  props: {
    itemId: {
      type: String,
      required: true,
    },
  },
};
</script>

Retrieve shapes and metadata

To retrieve the shapes and metadata specific to singular items, another API call has to be made. Import the itemApi function used earlier. For this specific example, the preview is expected to update as the user selects the different items in the grid. Create a new object called item that is empty by default, and assign the response from the API call to it.

In Preview.vue <script>:

import itemApi from '../api/item.api';
data() {
  return {
    item: {},
  };
},
watch: {
  itemId(newVal, oldVal) {
    if (oldVal === null || newVal !== oldVal) {
      itemApi.getItem(this.itemId).then((response) => {
        this.item = response;
      });
    }
  },
},

Adding image and metadata preview

An item can have multiple shapes, and displaying all of them at once would affect the system in an unnecessary way. The VdtImagePreview component requires a shape as an object to work, rendering the array of shapes received from the API call incompatible. Create a select input similar to the one in the import component, and list the available shapes there, and then send the selected shape the way of the VdtImagePreview component.

In Preview.vue:

<template>
  <div class="item-preview">
    <div v-if="item">
      <div class="preview-item">
        <select
          v-model="selectedShape">
            <option
              disabled
              value="">
              Select a shape
            </option>
            <option
              v-for="shape in item.shapes"
              :key="shape.id"
              :value="shape">
              {{ shape.tag }}
            </option>
          </select>
          <div v-if="selectedShape">
          <VdtImagePreview
            :shape="selectedShape"/>
        </div>
      </div>
    </div>
  </div>
</template>

data() {
  return {
    item: {},
    selectedShape: '',
  };
},

watch: {
  itemId(newVal, oldVal) {
    if (oldVal === null || newVal !== oldVal) {
      itemApi.getItem(this.itemId).then((response) => {
        this.item = response;
        [this.selectedShape] = [response.shapes[0]];
      });
    }
  },
},

Adding metadata preview

The VdtMetadata component requires an array of rows and the asset, as an object. The key within the row objects is the path to the metadata values and can vary depending on the database and metadata structuring. For now, let’s add four labels with corresponding paths.

In Preview.vue:

<div v-if="selectedShape">
  <VdtImagePreview
    :shape="selectedShape"/>
  <VdtMetadata
    :asset="item"
    :rows="rows"/>
</div>

data() {
  return {
    item: {},
    selectedShape: '',
    rows: [
      {
        label: 'Title',
        key: 'metadata.filename',
        type: 'String',
      },
      {
        label: 'Type',
        key: 'metadata.mediaType',
        type: 'String',
      },
      {
        label: 'Format',
        key: 'metadata.originalFormat',
        type: 'String',
      },
      {
        label: 'Created',
        key: 'metadata.created',
        type: 'Date',
      },
    ],
  };
},

Adding a video player

The component can now display images, as well as display the metadata related to the item being previewed. Let’s add the VdtVideoPlayer to enable the previewing of items with video formats as well, along with a computed property that returns the mediaType of the item. Using this property, it is now possible to display either the VdtImagePreview component or the VdtVideoPlayer component depending on the media type of the item.

In Preview.vue:

<div v-if="selectedShape">
  <VdtImagePreview
    v-if="itemType === 'image'"
    :shape="selectedShape"/>
  <VdtVideoPlayer
    v-if="itemType === 'video'"
    :shape="selectedShape"/>
  <VdtMetadata
    :asset="item"
    :rows="rows"/>
</div>

computed: {
  itemType() {
   return this.item.metadata.mediaType;
  },
},

Finally, apply some styling to enable scrolling in the preview document.

<style lang="scss" scoped>
.preview {
  width: 100%;
  height: 100vh;
  overflow: auto;
}
</style>

That concludes part 3, where we learned how to create a grid with items from the repository, as well as showing images, videos and metadata. In the next part of the tutorial, we will add transcoding and QC with cost estimates, as well as export functionality.