Cloudinary Blog

Build A Miniflix in 10 Minutes

By Prosper Otemuyiwa
Build A Miniflix in 10 Minutes

Developers are constantly faced with challenges of building complex products every single day. And there are constraints on the time needed to build out the features of these products.

Engineering and Product managers want to beat deadlines for projects daily. CEOs want to roll out new products as fast as possible. Entrepreneurs need their MVPs like yesterday. With this in mind, what should developers do?

In this tutorial, we’ll quickly build out a Mini Netflix in 10 minutes. In fact, I think we might build it less time.

MVP Challenge

An excited entrepreneur just approached you to build a video service. A service where users can quickly upload short videos and share on twitter for their friends to view. Let’s list out the features of this app.

Features

  • Users should be able to sign up and log in.
  • Registered/Logged-in users should be able to upload short videos of about 20 - 30 seconds.
  • Registered/Non-registered users should be able to view all uploaded videos on the platform on a dashboard.
  • Users should be able to share any of the videos on twitter.

Now, here’s the catch! T’challa of Wakanda wants to invest in some startups today, so the entrepreneur needs to demo the MVP of the video service in 10 minutes from now.

I know you are screaming your heart right now. It’s totally okay to cry and let the world know about your problems and challenges, but after much ado shedding tears, will the app be ready in 8 minutes? Well, sorry - tears can’t build an app!

Solution

It’s possible to build the MVP our entrepreneur is asking for. Let me show you how! Ready your editor, your command line and anxious fingers. Let’s get to work!!!

Step 1. Flesh Out The App

We’ll use React to build out the app. Facebook has a tool, create-react-app that can scaffold a progressive web app out of the box in less than a minute. If you don’t have it installed, please install and run the command below in your terminal:

Copy to clipboard
create-react-app miniflix
cd miniflix

Go ahead and open up public/index.html. Pull in bootstrap and add it just after the link to the favicon.

Copy to clipboard
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

Step 2. Set up Authentication & Views

Go ahead and install the following packages from your terminal:

Copy to clipboard
npm install auth0-js react-router@3.0.0 jwt-decode axios
  • auth0-js - For authentication
  • react-router - For routing within our app
  • jwt-decode - For decoding the JSON Web Token in our app
  • axios - For making network requests.

Open up your src directory and create a components and utils folder. In the utils folder, create a file, AuthService.js and add the code here to it. I explained how to handle the authentication in this tutorial, so check it out to ensure you are on the right track.

We’ll create 4 components in the components folder. Callback.js, Display.js, Nav.js and Upload.js

The Callback component basically stores our authentication credentials and redirects back to the upload route in our app.

The Display component will be dashboard for viewing all videos.

The Nav component will be the navigation that all pages in the app will share.

The Upload component will handle uploading of videos by registered users.

Add this piece of code below to components/Callback.js:

Copy to clipboard
import { Component } from 'react';
import { setIdToken, setAccessToken } from '../utils/AuthService';

class Callback extends Component {

  componentDidMount() {
    setAccessToken();
    setIdToken();
    window.location.href = "/";
  }

  render() {
    return null;
  }
}

export default Callback;

Add this piece of code to components/Nav.js:

Copy to clipboard
import React, { Component } from 'react';
import { Link } from 'react-router';
import { login, logout, isLoggedIn } from '../utils/AuthService';
import '../App.css';

class Nav extends Component {

  render() {
    return (
      <nav className="navbar navbar-default">
        <div className="navbar-header">
          <Link className="navbar-brand" to="/">Miniflix</Link>
        </div>
        <ul className="nav navbar-nav">
          <li>
            <Link to="/">All Videos</Link>
          </li>
          <li>
            {
             ( isLoggedIn() ) ? <Link to="/upload">Upload Videos</Link> :  ''
            }
          </li>
        </ul>
        <ul className="nav navbar-nav navbar-right">
          <li>
           {
             (isLoggedIn()) ? ( <button className="btn btn-danger log" onClick={() => logout()}>Log out </button> ) : ( <button className="btn btn-info log" onClick={() => login()}>Log In</button> )
           }
          </li>
        </ul>
      </nav>
    );
  }
}
export default Nav;

In the Nav component, you must have observed that we imported a css file. Open the App.css file and add this code here to it.

Add this piece of code to components/Display.js:

Copy to clipboard
import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';
import { isLoggedIn } from '../utils/AuthService';
import axios from 'axios';

class Display extends Component {

  render() {

    return (
      <div>
        <Nav />
        <h3 className="text-center"> Latest Videos on Miniflix </h3>
        <hr/>

        <div className="col-sm-12">

        </div>
      </div>
    );
  }
}

export default Display;

Add this piece of code to components/Upload.js:

Copy to clipboard
import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';

class Upload extends Component {


  render() {

    return (
      <div>
        <Nav />
        <h3 className="text-center">Upload Your 20-second Video in a Jiffy</h3>
        <hr/>

        <div className="col-sm-12">
          <div className="jumbotron text-center">
            <button className="btn btn-lg btn-info"> Upload Video</button>
          </div>
        </div>
      </div>
    );
  }
}

export default Upload;

Lastly, open up index.js and add replace it with the code here to set up your routes.

Now, when you run your app with npm start, you should have views like this:

MiniFlix1

MiniFlix2

Step 3. Upload Videos

We need a storage space for the videos our users will upload. Cloudinary is a cloud-based service that provides an end-to-end image and video management solution including uploads, storage, administration, manipulation and delivery. Head over to Cloudinary.com and create an account for free.

Let’s make use of Cloudinary’s Upload Widget. This widget allows you to upload videos or any type of file from your local computer, facebook, dropbox and Google Photos. Wow, very powerful. And the integration is seamless.

Go ahead and reference the cloudinary widget script in your index.html:

Copy to clipboard
 <script src="//widget.cloudinary.com/global/all.js" type="text/javascript"></script>

Note: You can add it just after the links.

Now in Upload.js, modify the code to look like this:

Copy to clipboard
import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';

class Upload extends Component {

  uploadWidget = () => {
    window.cloudinary.openUploadWidget(
      { cloud_name: 'cloud_name',
        upload_preset: '<unsigned-preset>',
        tags: ['miniflix'],
        sources: ['local', 'url', 'google_photos', 'facebook', 'image_search']
      },
      function(error, result) {
          console.log("This is the result of the last upload", result);
      });
  }

  render() {
    return (
      <div>
        <Nav />
        <h3 className="text-center">Upload Your 20-second Video in a Jiffy</h3>
        <hr/>

        <div className="col-sm-12">
          <div className="jumbotron text-center">
            <button onClick={this.uploadWidget} className="btn btn-lg btn-info"> Upload Video</button>
          </div>
        </div>
      </div>
    );
  }
}

export default Upload;

In the code above, we added a third argument, tags. Cloudinary provides this for automatic video tagging. Every video that is uploaded to this app will be automatically tagged, miniflix. In addition, you can provide as many tags as you want. This feature is very useful when you want to search for videos too!

In the uploadWidget function, we called the cloudinary.openUploadWidget function and attached it to the “Upload Video” button. When the user clicks the button, it opens the widget. Replace the cloud_name & upload_preset values with your credentials from Cloudinary dashboard.

Sign in to your app, head over to the upload videos route and try uploading a video.

MiniFlix3

Upload Widget

MiniFlix4

Uploading the video...

It uploads the video straight to Cloudinary and returns a response object about the recently uploaded video that contains so many parameters such as the unique public_id, secure_url, url, original_filename, thumbnail_url, created_at, duration and so many others.

Step 4. Display Videos

We need a dashboard to display all the videos uploaded for users to see at a glance. Here, we will make use of Cloudinary’s react component. Install it:

Copy to clipboard
npm install cloudinary-react

Now, open up components/Display.js and modify the code to be this below:

Copy to clipboard
import React, { Component } from 'react';
import { Link } from 'react-router';
import Nav from './Nav';
import { isLoggedIn } from '../utils/AuthService';
import { CloudinaryContext, Transformation, Video } from 'cloudinary-react';
import axios from 'axios';

class Display extends Component {

  state = { videos: [] };

  getVideos() {
    axios.get('https://res.cloudinary.com/unicodeveloper/video/list/miniflix.json')
          .then(res => {
            console.log(res.data.resources);
            this.setState({ videos: res.data.resources});
    });
  }

  componentDidMount() {
    this.getVideos();
  }

  render() {

    const { videos }  = this.state;

    return (
      <div>
        <Nav />
        <h3 className="text-center"> Latest Videos on Miniflix </h3>
        <hr/>

        <div className="col-sm-12">
          <CloudinaryContext cloudName="unicodeveloper">
            { videos.map((data, index) => (
                <div className="col-sm-4" key={index}>
                  <div className="embed-responsive embed-responsive-4by3">
                    <Video publicId={data.public_id} width="300" height="300" controls></Video>
                  </div>
                  <div> Created at {data.created_at} </div>

                </div>
              ))
            }
          </CloudinaryContext>
        </div>
      </div>
    );
  }
}

export default Display;

In the getVideos code above, we take advantage of a very slick Cloudinary trick that helps grab all videos with a particular tag, when using just one tag. Check it out again:

Copy to clipboard
https://res.cloudinary.com/unicodeveloper/video/list/miniflix.json

So we if had a tag like vimeo, our url will end up with .../vimeo.json. So in the code below, we got all the videos and stored in the videos state.

Copy to clipboard
axios.get('https://res.cloudinary.com/unicodeveloper/video/list/miniflix.json')
          .then(res => {
            console.log(res.data.resources);
            this.setState({ videos: res.data.resources});
    });

The Cloudinary React SDK has 4 major components, Image, Video, Transformation and CloudinaryContext. We are interested in the Video and CloudinaryContext for now. Christian explained how these components work here.

In the render method, we simply just looped through the videos state and passed the public_id of each video into the Cloudinary Video component. The Video component does the job of resolving the public_id from Cloudinary, getting the video url, and displaying it using HTML5 video on the webpage. An added advantage is this: Cloudinary automatically determines the best video type for your browser. Furthermore, it allows the user have the best experience possible by choosing the best from the range of available video types and resolutions.

Run your app, and try to see the list of all videos. It should be similar to this:

MiniFlix5

You can also manipulate your videos on the fly, with the help of Cloudinary via the Transformation component.

Step 5. Share on Twitter

Go ahead install the react twitter widget component:

Copy to clipboard
npm install react-twitter-widgets

In the components/Display.js file, import the component at the top:

Copy to clipboard
import { Share } from 'react-twitter-widgets'

Now, add this piece of code just after the div that shows the time the video was created.

Copy to clipboard


<Share url={`https://res.cloudinary.com/unicodeveloper/video/upload/${data.public_id}.mp4`} />

Check your app again. It should be similar to this:

MiniFlix6

Now, try to tweet.

MiniFlix7

Simple! It’s really not that hard. The source code for this tutorial is on GitHub.

Conclusion

Our MVP is ready. Our entrepreneur. Now sit back, relax and watch your account become flooded with investor money! Wait a minute, there is a 90% probability that you’ll called to add more features to this app. Well, I think Cloudinary can still help you with more features such as:

  • Automatic Subtitles and translation
  • Video briefs - short video, based on few gif images that will extract from the uploaded video.
  • Automatic and/or manual video markers - marking specific locations in the video so the user can wait patiently to watch them, or jump directly to these points
  • Find Similar videos by automatic video tagging

Cloudinary provides many options for uploading, transforming and optimizing your videos. Feel free to dive in and explore them.


This article was originally posted on Scotch.io


Prosper Otemuyiwa Prosper Otemuyiwa is a Food Ninja, Open Source Advocate & Self-acclaimed Developer Evangelist.

Recent Blog Posts

Our $2B Valuation

By
Blackstone Growth Invests in Cloudinary

When we started our journey in 2012, we were looking to improve our lives as developers by making it easier for us to handle the arduous tasks of handling images and videos in our code. That initial line of developer code has evolved into a full suite of media experience solutions driven by a mission that gradually revealed itself over the course of the past 10 years: help companies unleash the full potential of their media to create the most engaging visual experiences.

Read more
Direct-to-Consumer E-Commerce Requires Compelling Visual Experiences

When brands like you adopt a direct–to-consumer (DTC) e-commerce approach with no involvement of retailers or marketplaces, you gain direct and timely insight into evolving shopping behaviors. Accordingly, you can accommodate shoppers’ preferences by continually adjusting your product offering and interspersing the shopping journey with moments of excitement and intrigue. Opportunities abound for you to cultivate engaging customer relationships.

Read more
Automatically Translating Videos for an International Audience

No matter your business focus—public service, B2B integration, recruitment—multimedia, in particular video, is remarkably effective in communicating with the audience. Before, making video accessible to diverse viewers involved tasks galore, such as eliciting the service of production studios to manually dub, transcribe, and add subtitles. Those operations were costly and slow, especially for globally destined content.

Read more
Cloudinary Helps Minted Manage Its Image-Generation Pipeline at Scale

Shoppers return time and again to Minted’s global online community of independent artists and designers because they know they can count on unique, statement-making products of the highest quality there. Concurrently, the visual imagery on Minted.com must do justice to the designs into which the creators have poured their hearts and souls. For Minted’s VP of Engineering David Lien, “Because we are a premium brand, we need to ensure that every single one of our product images matches the selected configuration exactly. For example, if you pick an 18x24 art print on blue canvas, we will show that exact combination on the hero images in the PDF.”

Read more
Highlights on ImageCon 2021 and a Preview of ImageCon 2022

New year, same trend! Visual media will continue to play a monumental role in driving online conversions. To keep up with visual-experience trends and best practices, Cloudinary holds an annual conference called ImageCon, a one-of-a-kind event that helps attendees create the most engaging visual experiences possible.

Read more