Cloudinary Blog

Build A Miniflix in 10 Minutes

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.

Webinar
Delivering Compelling Video Experiences at Scale

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

Creating an API With Python Flask to Upload Files to Cloudinary

Code

Cloudinary offers SDKs for many programming languages and frameworks. Even though it also offers an Upload API endpoint for both back-end and front-end code, most developers find the SDKs very helpful. If you're working with a powerful back-end framework like Python Flask, you'll be happy to hear that a Python SDK is now available.
This tutorial walks you through the process of building an API to upload images to Cloudinary. You can also upload other file types, including video and even nonmedia files, with the API.

Read more
How to Use the Cloudinary Media Editor Widget

At Cloudinary, we manage the entire pipeline of media assets for thousands of customers of varying sizes from numerous verticals.

As part of our commitment to support the entire flow of media assets, we are now introducing an intuitive media editing widget: an out­-of­-the-­box, interactive UI providing your users with a set of common image editing actions for immediate use on your website or web app. The widget is interactive and simple, built on Cloudinary's transformation capabilities, and requiring only a few lines of code to integrate. Afterwards, you can seamlessly and effortlessly add content to your site or app with no need for in-house image editing capabilities.

Read more
Shoppable Video Is Becoming Popular in E-Commerce

As pandemic restrictions necessitated, many shopping trips in 2020 took place outside the traditional brick-and-mortar store, or at least void of the physical aisle-browsing experience. Same-day curbside pickup became a safe and convenient alternative, and e-commerce transactions skyrocketed as consumers shopped online. In fact, Digital Commerce 360 estimates that, compared to 2019, e-commerce transactions grew by more than 40% last year.

Read more
Enhance Your Travel Site With Cloudinary in Anticipation of a Return to New Normal

Read more
The Benefits of Headless DAMs

Headless is not a buzzword anymore. In fact, the concept of headless architecture is gaining momentum due to the flexibility it offers for composing new experiences and for tackling the undue complexity of an ever-evolving technology stack. That’s because while the evolution of the martech landscape has enabled disruptive, digital innovations, the approach of buying point solutions for solving specific challenges can expose companies to the complicated nature of new technologies, systems, and platforms.

Read more