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

By Filip - October 9, 2018 (Last updated: October 12, 2018)

Let’s wrap up the Vidispine Development Toolkit tutorial by adding transcode, QC and export functionality to our application. After concluding this you should have a basic understanding of VDT and what you can do with the Vidispine API.

Introduction

Welcome to the fourth part of the Vidispine Development Toolkit tutorial. In this part, we’ll add transcoding and QC functionality to the application, as well as a way to export your assets for use in other systems. When you have finished this part, you will have a complete, and very small, media supply chain, showing how you can take your assets from import to export using the Vidispine API and the VIdispine Development Toolkit.

This blog post

  • Set up and use the Vidispine transcoder from Vidinet (VdtTranscode)
  • Set up the Vidinet QC service (VdtVidinetQC)
  • Create a UI for exporting assets/shapes (VdtShapeExport)

VDT - Preview With Buttons

Building the application

Getting started

All the Vue components that we are going to create should be located in [my-project]/src/components. Use the default welcome page that is visible when we have just set up the Vidispine Development Toolkit is named VidispineVersion.vue 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 tutorialpart 2 to learn how to build the import component, and part 3 to learn how to list and preview items.

Setting up the transcoder

Start by creating a new file called ShapeAction.vue within the components folder. Import the necessary components and functions within the script tags. For this part we need VdtTranscode, VdtVidinetQC and VdtShapeExport.

<script>
// eslint-disable-next-line
import { VdtTranscode, VdtVidinetQC, VdtShapeExport } from '@vidispine/vdt-vue-components/es';
import shapeApi from '../api/shape.api';

export default {
  name: 'Actions',
  components: {
    VdtTranscode,
    VdtVidinetQC,
    VdtShapeExport,
  },
};
</script>

First of all, we need the transcoder up and running. The VdtTranscode component has quite a few required properties so hop on over to the VDT documentation and read up on its contents and requirements. Retrieve the item object from the parent component by declaring it as a required property and passing it on.

The URLs supplied here are exact strings containing the backend URL required to start an actual job and may vary somewhat depending on the services connected to Vidispine. The tags array is populated once a shape is selected through the mounted() function, and then updated each time the shape object updates through the watch function.

props: {
  item: {
    type: Object,
    required: true,
  },
  shape: {
    type: Object,
    required: true,
  },
},
data() {
  return {
    transcodeUrl: '/api/transcode',
    qcUrl: '/api/vidinet/qc',
    jobUrl: '/api/job/',
    tags: [],
  };
},
watch: {
  shape() {
    shapeApi.getShapeTags(this.shape.id).then((response) => {
      this.tags = response;
    });
  },
},
mounted() {
  shapeApi.getShapeTags(this.shape.id).then((response) => {
    this.tags = response;
  });
},

For this example, we are utilizing Bootstrap to enable the use of modal dialogs. Navigate to your application directory through and run:

yarn add bootstrap
yarn add bootstrap-vue 

To make sure that the packages are correctly added to your application, also run a yarn install to update the dependencies. We also make use of the buttons included in bootstrap for better integration with the modals.

Note: If you would like to use another package or none at all, you may skip this part of the guide.

In src/index.js, add;

import { Modal, Button } from 'bootstrap-vue/es/components';
Vue.use(Modal);
Vue.use(Button);

In src/assets/scss/base.scss, add;

@import '~bootstrap/scss/bootstrap-reboot';
@import '~bootstrap/scss/utilities/';
@import '~bootstrap/scss/_buttons';
@import '~bootstrap/scss/_close';
@import '~bootstrap/scss/_modal';

Within the new ShapeActions document, add a button in the HTML template and bind the modal to it. Then add the markup for the transcode component with the required props. The lazy prop on the modal removes the HTML markup from the DOM when the modal is hidden. The ID and name of the files in this example are retrieved from the path shown below and may vary depending on the structure of the metadata. These strings are required for the API to initiate jobs with specific items or shapes.

<template>
  <div class="buttons">
    <b-btn
      v-b-modal.TranscodeModal
      size="lg">
      Transcode
    </b-btn>
    <b-modal
      id="TranscodeModal"
      title="Transcode"
      lazy>
      <VdtTranscode
        :item-id="item.metadata.id"
        :filename="item.metadata.filename"
        :tags="tags"
        :cost-estimate-url="transcodeUrl"
        :start-transcode-url="transcodeUrl"
        :job-status-url="jobUrl"
        :abort-job-url="jobUrl"/>
    </b-modal>
  </div>
</template>

Finally,  add the ShapeAction to the Preview. In the Preview.vue document, import the ShapeActions component and supply the required props.

<template>
<!-- add this under VdtImagePreview/VdtVideoPlayer in the same div-->
<div class="shape-actions">
  <ShapeActions
    :item="item"
    :shape="selectedShape"/>
</div>
</template>
<script>
// import the ShapeActions
import ShapeActions from './ShapeActions.vue';

// and add the components together with VdtShape,VdtMetadata,VdtImagePreview, and VdtVideoPlayer
components: {
  ShapeActions,
},

</script>

Clicking the Transcode button will get you a dialogue where you select transcode preset, get a cost estimate of the operation, and then given the choice to do the transcode or not.

VDT - Transcode Cost Estimate Modal

With the transcode component running, we can now go ahead and add functionality for both the Quality Control and Export components. First, create a mounted() function that handles an API call to retrieve the available resources, this is a required prop for the QC component.

For instructions on how to properly pass a URL to the API call, see the Vidispine documentation. For this API call to work, import Axios and create an empty array. Then, add two new buttons to go along with two new modals containing the components.

<script>
import axios from 'axios';

data() {
  return {
    transcodeUrl: '/api/transcode',
    qcUrl: '/api/vidinet/qc',
    jobUrl: '/api/job/',
    exportLocationUrl: '/api/export-location',
    startExportUrl: '/api/shape-export',
    tags: [],
    resources: [],
  };
},

mounted() {
  shapeApi.getShapeTags(this.shape.id).then((response) => {
    this.tags = response;
  });
  axios.get('/api/resource?resourceType=vidinet').then((response) => {
    this.resources = response.data.resource;
  });
},
</script>

Finally, add a bit of styling to ShapeActions.vue to evenly display the shape options.

<style lang="scss" scoped>
.buttons {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-column-gap: 1em;
}
</style>

That was the frontend part of it. To have a location to export to, you also need to create a new export location in Vidispine. Creating the export location is done using an API call to your Vidispine instance with a description of the export location, for example in an S3 bucket like below.

PUT /API/export-location/myexport
Content-Type: application/xml
<ExportLocationDocument xmlns="http://xml.vidispine.com/schema/vidispine">
 <uri>s3://{accessKey}:{secretKey}@{bucket}/{path}/?region={your-region}</uri>
</ExportLocationDocument>

With that you should have a working export button, and get a modal like the one below if you press it, where you can select among your export locations.

VDT - Export Modal Selecting Export Location

In the end, the entire ShapeActions.vue should look similar to the example below;

<template>
  <div class="buttons">
    <b-btn
      v-b-modal.TranscodeModal
      size="md">
      Transcode
    </b-btn>
    <b-btn
      v-b-modal.QcModal
      size="md">
      Quality Control
    </b-btn>
    <b-btn
      v-b-modal.ExportModal
      size="md">
      Export
    </b-btn>
    <b-modal
      id="TranscodeModal"
      title="Transcode"
      lazy>
      <VdtTranscode
        :item-id="item.metadata.id"
        :filename="item.metadata.filename"
        :tags="tags"
        :cost-estimate-url="transcodeUrl"
        :start-transcode-url="transcodeUrl"
        :job-status-url="jobUrl"
        :abort-job-url="jobUrl"/>
    </b-modal>
    <b-modal
      id="QcModal"
      title="Quality Control"
      button-size="md"
      lazy>
      <VdtVidinetQC
        :item-id="item.metadata.id"
        :shape-id="shape.id"
        :shape-tag="shape.tag"
        :resources="resources"
        :cost-estimate-url="qcUrl"
        :start-qc-url="qcUrl"
        :job-status-url="jobUrl"
        :abort-job-url="jobUrl"/>
    </b-modal>
    <b-modal
      id="ExportModal"
      title="Export"
      button-size="md"
      lazy>
      <VdtShapeExport
        :item-id="item.metadata.id"
        :shape-id="shape.id"
        :tag="shape.tag"/>
        :export-location-url="exportLocationUrl"
        :start-export-url="startExportUrl"
        :job-status-url="jobUrl"
        :abort-job-url="jobUrl"
        :poll-interval="4000"/>
    </b-modal>
  </div>
</template>

<script>
import axios from 'axios';
// eslint-disable-next-line
import { VdtTranscode, VdtVidinetQC, VdtShapeExport } from '@vidispine/vdt-vue-components/es';
import shapeApi from '../api/shape.api';

export default {
  name:'Actions',
  components: {
    VdtTranscode,
    VdtVidinetQC,
    VdtShapeExport,
  },
  props: {
   item: {
      type:Object,
      required:true,
    },
    shape: {
      type:Object,
      required:true,
    },
  },
  data() {
    return {
      transcodeUrl:'/api/transcode',
      qcUrl:'/api/vidinet/qc',
      jobUrl:'/api/job/',
      tags: [],
      resources: [],
    };
  },
  watch: {
    shape() {
       shapeApi.getShapeTags(this.shape.id).then((response) => {
         this.tags=response;
      });
    },
  },
  mounted() {
    shapeApi.getShapeTags(this.shape.id).then((response) => {
      this.tags=response;
    });
    axios.get('/api/resource?resourceType=vidinet').then((response) => {
      this.resources=response.data.resource;
    });
  },
};
</script>

<style lang="scss" scoped>
.buttons {
  display: grid;
  grid-template-columns: 1fr1fr1fr;
  grid-column-gap: 1em;
}
</style>

Now what?

This should have given you some insight into how to build applications using the Vidispine API and the Vidispine Development Kit. Even though this tutorial is finished, we already have a few new parts based on this same simple application to show you what else can be done.

You can also clone the full code for the Vidispine Content Viewer running in your Vidispine API Starter Edition to learn more about how to build using VDT.

VDT - Final UI