Video Moderation with Cloudinary

Eugene Musebe

Introduction

Video moderation helps you share highly engaging and effective video content while protecting your brand, customers and profits. We will create a simple automated video moderation platform that will help us filter out offensive videos.

Let's get started.

PHPSandbox and Github

The final project can be viewed on PHPSandbox and the entire source code is available on my Github repository.

Prerequisites

Using Cloudinary in your Laravel projects is pretty straightforward. However, for you to be able to easily follow along, you need to have a good command of your terminal, Git and entry knowledge of PHP specifically with the Laravel framework.

Getting Started

Being that Laravel is a PHP Framework, we will need Composer. Like any modern PHP framework, Laravel uses Composer to manage its dependencies. So, before we can start ensure you have Composer installed on your machine. Follow step 1 below to install Composer and PHP.

  1. Install Composer and PHP on

your development or production machine.

  1. Install Laravel

  2. Via Composer:

composer create-project --prefer-dist laravel/laravel cloudinary-video-slideshow

  1. Via Laravel Installer

composer global require laravel/installer

laravel new cloudinary-video-slideshow

  1. In step 2 above we have installed the Laravel Installer and used it to scaffold a new application in the folder cloudinary-video-slideshow. With Laravel installed, we should be able to start and test the server ensuring everything is okay. Change the directory to the project folder and run the local development server by typing the following commands:

cd cloudinary-video-slideshow

php artisan serve

The Laravel project is now up and running. When you open http://localhost:8000 on your computer, you should see the image below:

Setting up Cloudinary’s Laravel SDK

Cloudinary has a tonne of features from media upload, storage, administration, manipulation to optimization and delivery. In this article, we will use Cloudinary Video Transformations to combine existing or newly uploaded media files to create a slideshow.

  1. Sign up for a free Cloudinary account then navigate to the Console page and take note of your Cloud name, API Key and API Secret.

  1. Google AI Video Moderation Addon - while at the Cloudinary dashboard, click on the Addons menu and subscribe for the Google AI Video Moderation Addon.

  1. Install Cloudinary’s Laravel SDK:

composer require cloudinary-labs/cloudinary-laravel

Note: Please ensure you follow all the steps in the #Installation section. Publish the configuration file and add

the Cloudinary credentials you noted in Step 1 to the .env file.

1CLOUDINARY_API_KEY=YOUR_CLOUDINARY_API_KEY
2
3CLOUDINARY_API_SECRET=YOUR_CLOUDINARY_API_SECRET
4
5CLOUDINARY_CLOUD_NAME=YOUR_CLOUDINARY_CLOUD_NAME

Cloudinary Video Moderation

Cloudinary employs two addons for video moderation:

  1. Rekognition AI Video Moderaion by AWS

  2. Google AI Video Moderation

Google assigns a moderation confidence level indicating the chances that a video contains unacceptable content. The likelihood is given as a value on the following scale: very_unlikely, unlikely, possible, likely, and very_likely.

To send a request for Google AI Video Moderation with the default rejection confidence level we need to set the moderation parameter to google_video_moderation and set the resource_type to video:

$cloudinary->uploadApi()->upload("my_file.mp4", [ "resource_type" => "video", "moderation" => "google_video_moderation"]);

Let's see this in code.

File Upload with Livewire

To uplaod a video for moderation we will need a UI (User Interface), we will use the Laravel package Livewire to build this.

  1. Install Livewire Package by running the following command in your Laravel project:

composer require livewire/livewire

  1. Include Livewire scripts and styles on every page that will be using Livewire. In our case welcome.blade.php:
1...
2
3@livewireStyles
4
5</head>
6
7<body>
8
9...
10
11@livewireScripts
12
13</body>
14
15</html>
  1. We will then create a Livewire Component to handle our image uploads:

php artisan make:livewire FileUpload

This will create two files, first app/Http/Livewire/FileUpload.php and the other one

in resources/views/livewire/multiple-file-upload.blade.php

Now you can use this component anywhere in your Laravel project using the following snippet:

<livewire:file-upload/>

or

@livewire('file-upload')

  1. Open resources/views/welcome.blade.php and add the following code within the <body></body> tags as shown below:
1<body class="antialiased">
2
3<div>
4
5@livewire('file-upload')
6
7</div>
8
9</body>

This includes the Livewire component we created earlier in our welcome.blade.php.

Note: Please ensure you go through the Livewire documentation, to learn how to install and set it up.

  1. Open the file resources/views/livewire/multiple-file-upload.blade.php and populate it with the following code:
1<form class="mb-5" wire:submit.prevent="uploadVideo">
2
3<div class="form-group row mt-5 mb-3">
4
5<div class="input-group">
6
7<input id="video" type="file" class="form-control @error('video') is-invalid @enderror" wire:model="video">
8
9@error('video')
10
11<div class="invalid-feedback">{{ $message }}</div>
12
13@enderror
14
15</div>
16
17<small class="text-muted text-center mt-2" wire:loading wire:target="video">
18
19{{ __('Uploading') }}&hellip;
20
21</small>
22
23</div>
24
25<div class="text-center">
26
27<button type="submit" class="btn btn-sm btn-primary">
28
29{{ __('Upload Video') }}
30
31<i class="spinner-border spinner-border-sm ml-1 mt-1" wire:loading wire:target="uploadVideo"></i>
32
33</button>
34
35</div>
36
37</form>

This is our Livewire Component view, this basically will display a form with a file input and a button.

You will see the implementation in code shortly.

Implementation in Code

Open the file app/Http/Livewire/FileUpload.php. Here, we are going to add a method that will handle the video selected by the user, upload them to Cloudinary for video moderation.

Add the following code to this file.

  1. First, we use Livewires WithFileUploads to help us with file uploads, then create the variable $video.
1use Livewire\WithFileUploads;
2
3public $video;
  1. Secondly, we will create the uploadVideo function which will upload the video to Cloudinary. We will add the folder, resource_type, notification_url and moderation which will moderate the video.
1public function uploadVideo() {
2
3...
4
5}
  1. Let's populate our method in step 2 above:
1public function uploadFiles() {
2
3/* First we validate the input from the user. We will take a video file less than 10MB in size */
4
5$this->validate([
6
7'video' => [
8
9'required',
10
11'file',
12
13'mimes:avi,mp4,webm,mov,ogg,mkv,flv,m3u8,ts,3gp,wmv,3g2,m4v',
14
15'max:102400'
16
17],
18
19]);
20
21/* Upload video to Cloudinary for moderation */
22
23cloudinary()->upload($this->video->getRealPath(), [
24
25'folder' => 'video-mod',
26
27'resource_type' => 'video',
28
29'moderation' => 'google_video_moderation:possible',
30
31'notification_url' => env('CLOUDINARY_NOTIFICATION_URL')
32
33]);
34
35session()->flash('message', "Video moderation initiated successfully!");
36
37}

The code above performs validation then uploads the video file to Cloudinary for moderation.

On successful implementation, you should be able to see the following when you navigate to http://localhost:8000:

When you successfully upload a video with questionable content you will receive two responses at the notification_url we have set.

First response will be the upload notification with a pending moderation status:

1{
2
3"notification_type": "upload",
4
5"timestamp": "2022-07-03T08:50:25+00:00",
6
7"request_id": "200c27d8d22acf7445ea1c759775bdb1",
8
9"asset_id": "62a5e294973044fd95df8eb1887a4db3",
10
11"public_id": "video-mod/PjtS3MQJUKYZzQ88GJoJMwhEkue2ND-meta5oqW6Z_zLeiusOW9lee_juWlveeUn_a0uy5tcDQ_-_crj35i",
12
13"version": 1656838225,
14
15"version_id": "fefb04a8f3cd95100600cd3d8e3bdebe",
16
17"width": 1280,
18
19"height": 720,
20
21"format": "mp4",
22
23"resource_type": "video",
24
25"moderation": [
26
27{
28
29"status": "pending",
30
31"kind": "google_video_moderation"
32
33}
34
35],
36
37...
38
39}

The second response will be the moderation notification. This contains a bunch of data. Take note of the moderation_status which will let you know whether the video passed the moderation or not.

1{
2
3"moderation_response": {
4
5"moderation_confidence": "POSSIBLE",
6
7"frames": [
8
9{
10
11"pornography_likelihood": "POSSIBLE",
12
13"time_offset": 0.769962
14
15},
16
17{
18
19"pornography_likelihood": "POSSIBLE",
20
21"time_offset": 1.891071
22
23},
24
25{
26
27"pornography_likelihood": "POSSIBLE",
28
29"time_offset": 2.730552
30
31},
32
33{
34
35"pornography_likelihood": "LIKELY",
36
37"time_offset": 3.888789
38
39},
40
41{
42
43"pornography_likelihood": "LIKELY",
44
45"time_offset": 4.755875
46
47},
48
49{
50
51"pornography_likelihood": "VERY_LIKELY",
52
53"time_offset": 5.77656
54
55},
56
57{
58
59"pornography_likelihood": "LIKELY",
60
61"time_offset": 6.650433
62
63},
64
65{
66
67"pornography_likelihood": "VERY_LIKELY",
68
69"time_offset": 7.55418
70
71},
72
73{
74
75"pornography_likelihood": "LIKELY",
76
77"time_offset": 8.730771
78
79}
80
81]
82
83},
84
85"moderation_status": "rejected",
86
87"moderation_kind": "google_video_moderation",
88
89"notification_type": "moderation",
90
91...
92
93}

You can checkout the documentation for more details.

Handling Cloudinary Responses

Webhooks are one of a few ways web applications can communicate with each other. We can receive Cloudinary's responses through a webhook and run processes that will do something like notify the user or ban the video.

Create a WebhookController.php by typing the following command:

php artisan make:controller WebhookController

In the file created app/Http/Controllers/WebhookController.php we will add the following code:

1public function cloudinary(Request $request) {
2
3//Verification
4
5$verified = SignatureVerifier::verifyNotificationSignature(json_encode($request), $request->header('X-Cld-Timestamp'), $request->header('X-Cld-Signature'));
6
7
8
9// If the signature is verified and moderation is rejected
10
11if ($verified && $request->moderation_status === 'rejected') {
12
13// Ban video
14
15...
16
17// Notify user
18
19...
20
21}
22
23
24
25return response('Unverified', 401);
26
27}

Tip: A webhook is a mechanism where an application can notify another application that something has happened.

Since the notification from Cloudinary will be an external request we will need to allow it through the VerifyCsrfToken.php middleware to prevent CSRF errors.

1...
2
3protected $except = [
4
5'webhooks'
6
7];
8
9}

Next, we will create the webhook route in routes/api.php.

1...
2
3//webhooks client
4
5Route::post('webhooks/cloudinary', [WebhookController::class, 'cloudinary']);

And finally update our CLOUDINARY_NOTIFICATION_URL in the environment variables file .env as follows:

1CLOUDINARY_NOTIFICATION_URL=https://<app_url>/api/webhooks/cloudinary

Conclusion

Cloudinary allows you to automatically moderate all videos uploaded to your application using Google's AI Moderation. This is vital in protecting your users from explicit and suggestive adult content in videos on your website or mobile application.

Using Cloudinary's upload and delivery APIs we can have peace of mind knowing that videos uploaded on our platforms will be automatically approved for user viewing.

Check out Cloudinary for your A to Z media management - upload, storage, administration, manipulation, optimization and delivery.

Get started with Cloudinary in your Laravel projects for FREE!

Eugene Musebe

Software Developer

I’m a full-stack software developer, content creator, and tech community builder based in Nairobi, Kenya. I am addicted to learning new technologies and loves working with like-minded people.