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:

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.

Upload Widget

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:

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:

Now, try to tweet.

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

Generate Waveform Images from Audio with Cloudinary

This is a reposting of an article written by David Walsh. Check out his blog HERE!
I've been working a lot with visualizations lately, which is a far cry from your normal webpage element interaction coding; you need advanced geometry knowledge, render and performance knowledge, and much more. It's been a great learning experience but it can be challenging and isn't always an interest of all web developers. That's why we use apps and services specializing in complex tasks like Cloudinary: we need it done quickly and by a tool written by an expert.

Read more
Make All Images on Your Website Responsive in 3 Easy Steps

Images are crucial to website performance, but most still don't implement responsive images. It’s not just about fitting an image on the screen, but also making the the image size relatively rational to the device. The srcset and sizes options, which are your best hit are hard to implement. Cloudinary provides an easier way, which we will discuss in this article.

Read more

The Future of Audio and Video on the Web

By Prosper Otemuyiwa
The Future of Audio and Video on the Web

Web sites and platforms are becoming increasingly media-rich. Today, approximately 62 percent of internet traffic is made up of images, with audio and video constituting a growing percentage of the bytes.

Read more

Embed Images in Email Campaigns at Scale

By Sourav Kundu
Embed Images in Email Campaigns at Scale

tl;dr

Cloudinary is a powerful image hosting solution for email marketing campaigns of any size. With features such as advanced image optimization and on-the-fly image transformation, backed by a global CDN, Cloudinary provides the base for a seamless user experience in your email campaigns leading to increased conversion and performance.

Read more
Build the Back-End For Your Own Instagram-style App with Cloudinary

Github Repo

Managing media files (processing, storage and manipulation) is one of the biggest challenges we encounter as practical developers. These challenges include:

A great service called Cloudinary can help us overcome many of these challenges. Together with Cloudinary, let's work on solutions to these challenges and hopefully have a simpler mental model towards media management.

Read more

Your Web Image is Unnecessarily Bloated

By Christian Nwamba
Your Web Image is Unnecessarily Bloated

Images make up a majority of web content.But a lot of websites and applications that we browse aren’t always delivering the most optimal image format, size, quality and dimension.. . .

As a developer, it seems inefficient to serve a 2000kb JPEG image when we could compress images to optimize the performance without degrading the visual quality.

Read more