How to Restore Dislikes on YouTube with Django Rest Framework and YoutubeV3 API.
Tags: api, djangorestframework, django, youtubeapi
Published: 2021-12-01 15:20:50

## **UPDATE**
Youtube has taken down the view count API and this functionality is no longer functional as of 13th December

## Overview:

Earlier this month, video sharing service YouTube, which is now the second-most-used platform online, announced that while the "dislike" button would remain, the actual dislike count would be made private.

"To ensure that YouTube promotes respectful interactions between viewers and creators, we introduced several features and policies to improve their experience," the company announced via a blog post. "And earlier this year, we experimented with the dislike button to see whether or not changes could help better protect our creators from harassment, and reduce dislike attacks — where people work to drive up the number of dislikes on a creator’s videos."

While the removal of public dislikes could help the creators, there is the argument this is also about helping YouTube by getting more eyeballs on videos – even ones that could disappoint some viewers. The lack of "dislikes" means there is no quick public review of the user-generated content.

## Introduction

In this article, we will see how we can display the dislike count of any youtube video using [YoutubeV3 API](https://developers.google.com/youtube/v3) and Django Rest Framework.

To test my live system, head over to a youtube video of your choice on the web: 

 - Replace "[www.youtube.com](www.youtube.com)":

![yes1.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638355114034/VuOSSjtZW.png)

 - With "[alienx.tech](https://alienx.tech/)":

![as.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638361519307/CfPi5bhlY.png)

Be sure not to change the video-ID and ensure you remove the "www" otherwise you will get a privacy error, then hit enter.

If successful, your browser will redirect to the page below which displays both the like and dislike count:

![3.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638310748702/F8z_d7YAj.png)

Before we dive into building our microservice, let us start by understanding how I was able to achieve this.

## How it Works

It is worth stating that I got this idea from [y2mate](https://www.y2mate.com/) which is a free youtube downloader website that works by inserting "pp" after the word "youtube" in the link, like so:

![tip-download-youtube.jpg](https://cdn.hashnode.com/res/hashnode/image/upload/v1638357694789/4iaKAKvsB.jpeg)

The first thing I needed to do was to create an API route to be like youtube is on my Django backend. I also needed the youtube API for collecting the data about the video at play. Surprisingly, youtube has not made that functionality private which is fair enough.

I did not find Youtubes' [API docs](https://developers.google.com/youtube/v3/docs) to be user friendly and so I did some digging where I came across this god-sent freemium app on [RapidAPI](https://rapidapi.com/ytdlfree/api/youtube-v31/) that enables you to use the youtube V3 API without having an API key from Google developers account! The app is very well maintained and has a low ping. PS: I was able to tell it's an awesome API by just looking at the ranking score it got on rapid API so negative feedback can actually help save you money;). Imagine subscribing to a paid API that turns out to be very slow yet you could have looked at the like to dislike ratio.

On my Django Rest backend side, I needed [prometheus](https://blog.alienx.tech/introduction-what-is-prometheus),(the name of my API server), to make a request to youtubeV3 API, then return the response to the user as a template, not json or any other format. Django being the most powerful web development framework, in my opinion, has an inbuilt function for doing this called [Renderers](https://www.django-rest-framework.org/api-guide/renderers/). The rendering process works by taking the intermediate representation of the template and context and turning it into the final byte stream that can be served to the client.

## Building our API

For this tutorial, we shall be using python3.5+, Django Rest Web Framework, and Youtube's v3 API for pulling youtube video data including the dislike count.

Following Python best practices, we will create a virtual environment for our project, and install the required packages.

First, create the project directory.

```
$ mkdir djangoapp
$ cd djangoapp

``` 

Now, create a virtual environment and install the required packages.

For macOS and Unix systems:

```
$ python3 -m venv myenv
$ source myenv/bin/activate
(myenv) $ pip3 install django djangorestframework requests
```
For Windows:

```
$ python3 -m venv myenv
$ myenv\Scripts\activate
(myenv) $ pip3 install django djangorestframework requests
```

### Youtube V3 API

As mentioned earlier, I will not be accessing youtube's API using my own access token, I will be using a third-party app found on rapid API which is one of the largest API marketplaces on the web. Create an account with rapid API to get the freemium [youtubeV3API](https://rapidapi.com/ytdlfree/api/youtube-v31)

### Setting Up Your Django Application

First, navigate to the directory Django app we created and establish a Django project.

```
(myenv) $ django-admin startproject mainapp

```

This will auto-generate some files for your project skeleton:

```
mainapp/
    manage.py
    mainapp/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py
```

Now, navigate to the directory you just created (make sure you are in the same directory as manage.py) and create your app directory.

```
(myenv) $ python manage.py startapp exam
```

This will create the following:

```
exam/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py
```

On the mainapp/settings.py file, look for the following line and add the app we just created above.

```
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.humanize',#new line, explanation later
    'rest_framework',#new line
    'exam', #new line
]

```

Ensure you are in the exam directory then create a new directory called templates and a new file called urls.py. Your directory structure of exam application should look like this

```
exam/
    __init__.py
    admin.py
    apps.py
    migrations/
    templates/
        __init__.py
    models.py
    tests.py
    urls.py
    views.py
```

Ensure your mainapp/urls.py file, add our exam app URL to include the URLs we shall create next on the exam app:

```
"""mainapp URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path(' ', include('exam.urls')),#exam app url
]

```

Now, on the exam/urls.py file, add our website there:

```
from django.urls import path, include
from . import views

urlpatterns = [
    path('watch', YoutubeStats.as_view(), name="youtubestats"),
]
```

On the exam/views.py, add the following lines of code: 

```
from django.shortcuts import render, redirect
import requests
from django.http import HttpResponse, HttpResponseRedirect
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.renderers import TemplateHTMLRenderer

class YoutubeStats(APIView):
      renderer_classes = [TemplateHTMLRenderer]
      template_name = 'youtube.html'

      def get(self, request):
          toScan = request.GET.get('v') #get users request(video URL in this case)
          #youtubev3 api
          parsed = str(toScan) #convert to string
          url = 'https://youtube-v31.p.rapidapi.com/videos'
          querystring = {"part":"contentDetails,snippet,statistics","id":parsed} 
          headers = {'x-rapidapi-host': "youtube-v31.p.rapidapi.com",'x-rapidapi-key': "GETYOURKEY"}
          response = requests.request("GET", url, headers=headers, params=querystring)
          DataResponse = response.json() 
          items = DataResponse.get('items')
          for data in items:
              chanid = data.get('snippet')
              channelid = chanid['channelId']
          #context = {'items':items, 'videoid':parsed}
          url2 = "https://youtube-v31.p.rapidapi.com/channels"
          querystring2 = {"part":"snippet,statistics","id":channelid}
          response2 = requests.request("GET", url2, headers=headers, params=querystring2)
          DataResponse2 = response2.json()
          items2 = DataResponse2.get('items')
          context = {'items':items, 'videoid':parsed, 'items2':items2,}
          return Response(context)
```

### Explanation of views.py

In the code above, we start by getting initiating the template class and template name of our API. We then get the request of the user that was appended to the video to be analyzed ('v'). We then proceed by converting the requested videoID to a string and then making a request to the youtube API. 

The sample response from youtube API looks like so:

```
{
   "kind":"youtube#videoListResponse",
   "items":[
      {
         "kind":"youtube#video",
         "id":"frn3WT15Dy8",
         "snippet":{
            "publishedAt":"2021-11-23T01:30:12Z",
            "channelId":"UCPWXiRWZ29zrxPFIQT7eHSA",
            "title":"'This Is Now Becoming A Pattern': Jordan Slams YouTube Covid-19 'Censorship'",
            "description":"During a Wednesday hearing on COVID-19 misinformation, Rep. Jim Jordan (R-OH) criticized YouTube for removing a video containing 'misinformation.' The Ohio Republican said the social media company should not be censoring doctors over 'science.'",
            "thumbnails":{
               "default":{
                  "url":"https://i.ytimg.com/vi/frn3WT15Dy8/default.jpg",
                  "width":120,
                  "height":90
               },
               "medium":{
                  "url":"https://i.ytimg.com/vi/frn3WT15Dy8/mqdefault.jpg",
                  "width":320,
                  "height":180
               },
               "high":{
                  "url":"https://i.ytimg.com/vi/frn3WT15Dy8/hqdefault.jpg",
                  "width":480,
                  "height":360
               },
               "standard":{
                  "url":"https://i.ytimg.com/vi/frn3WT15Dy8/sddefault.jpg",
                  "width":640,
                  "height":480
               },
               "maxres":{
                  "url":"https://i.ytimg.com/vi/frn3WT15Dy8/maxresdefault.jpg",
                  "width":1280,
                  "height":720
               }
            },
            "channelTitle":"The Hill",
            "categoryId":"25",
            "liveBroadcastContent":"none",
            "defaultLanguage":"en-US",
            "localized":{
               "title":"'This Is Now Becoming A Pattern': Jordan Slams YouTube Covid-19 'Censorship'",
               "description":"During a Wednesday hearing on COVID-19 misinformation, Rep. Jim Jordan (R-OH) criticized YouTube for removing a video containing 'misinformation.' The Ohio Republican said the social media company should not be censoring doctors over 'science.'"
            },
            "defaultAudioLanguage":"en-US"
         },
         "contentDetails":{
            "duration":"PT6M19S",
            "dimension":"2d",
            "definition":"hd",
            "caption":"false",
            "licensedContent":true,
            "contentRating":{
               
            },
            "projection":"rectangular"
         },
         "statistics":{
            "viewCount":"989839",
            "likeCount":"35804",
            "dislikeCount":"798",
            "favoriteCount":"0",
            "commentCount":"7034"
         }
      }
   ],
   "pageInfo":{
      "totalResults":1,
      "resultsPerPage":1
   }
}
```

In order to get the title and subscriber count of the channel owner, we need to make another request to the channels API. We append the channel_ID to the querystring2 and then make a request to the channels API. We then proceed to render the context as the final response on our HTML template that we shall create below.

### Youtube Template

On the exam/templates/ folder, create a youtube.html file and add the following lines of code: 

```
{% load humanize %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css"
    />
    <style>
    @import url(https://fonts.googleapis.com/css2?family=Roboto&display=swap);*{margin:0;padding:0;box-sizing:border-box}body{background-color:rgba(200,200,200,.1);font-family:Roboto,sans-serif}.icon-spacing{padding:0 10px}.flex-column{flex-direction:column}.space-between{justify-content:space-between}.grid-container{position:relative;left:250px}nav{align-items:center;position:fixed;top:0;width:100%;height:50px;background-color:#fff;justify-content:space-between;z-index:1}#left-navbar{justify-content:space-evenly;width:12%;align-items:center;margin-left:15px}#mid-navbar{align-self:center;margin-right:20px;width:50%;justify-content:center}#search-bar{background-color:#fff;width:85%;padding:3px 10px;border:1px solid silver;border-radius:3px 0 0 3px;font-size:medium}#search-button{border:1px solid gray;padding:0 30px;border-radius:0 3px 3px 0}#right-navbar{align-items:center;width:120px;justify-content:space-between;margin-right:50px}.main-video{margin-left:initial}.flex{display:flex}.upcoming{font-family:Roboto,sans-serif;font-size:16px;color:#030303}.input{width:550px;position:relative;left:219px}.container{position:relative;top:85px;left:22px}.main-fc{width:60%;margin-left:30px}.other-videos{width:30%}#autobutton{border:none;background-color:transparent}.sidebar{position:relative;top:80px}.image{float:left;height:10px;position:relative;width:168px}.aside-video{grid-template-columns:180px 180px;display:grid;margin-bottom:4px}.song-title{font-size:14px;color:#030303;display:inline-block}.aside-description{position:relative;bottom:8px;font-size:12px}.artist-name{font-size:13px;color:#606060}.aside-views{font-size:13px;color:#606060}.video-duration{position:relative;float:right;bottom:18px;right:14px;font-size:12px;background-color:#000;color:#f5f5f5}.video-title{font-size:1.15rem;font-weight:300}.images{border-radius:50%;width:40px;height:40px}.smaller-gray{font-size:.9rem;color:gray}#video-below{justify-content:space-between;align-items:center}.video-section{justify-self:start}.subscribe-button{float:right;margin-top:-60px;background-color:#c00;font-size:smaller;border:none;color:#fff;padding:10px}.align-center{align-items:center;align-content:center;align-self:center}.inline{display:inline}.upnext{justify-content:center}.sidebar-attributes{font-family:Roboto,sans-serif;line-height:1.2em}.page-profile p{font-size:14px}#Squatty-potty{margin-bottom:-5px;padding-bottom:5px;margin-top:-20px}#avatar{margin-right:16px;margin-top:-40px;width:50px;height:50px}.user-name-bolded{margin:0 10px;font-size:small}.comment-description{margin-left:50px;margin-top:-20px;font-size:smaller}.smallest-gray{font-size:.7rem;color:gray}.reply{margin-left:10px;font-weight:300}.replies{margin-top:10px}.gray{color:#636363}.sort-button{display:grid;grid-template-columns:1fr 4.5fr}.sort-by{margin-left:10px}button{border:none;background-color:transparent}
    </style>
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
    />
    <title>Youtube Page</title>
  </head>

  <body>
    <nav class="flex">
      <div id="left-navbar" class="flex">
        <a href="#"><img src="https://res.cloudinary.com/prometheusapi/image/upload/v1638303419/hamburger_v2ird3.png" alt="" /></a>
        <a href="#"><img src="https://res.cloudinary.com/prometheusapi/image/upload/v1638303419/youtubelogos_kkvpsz.png" alt="" /></a>
      </div>
      <div id="mid-navbar" class="flex">
        <input id="search-bar" type="text" placeholder="Search" />
        <button id="search-button">
          <i class="fa fa-search" aria-hidden="true"></i>
        </button>
      </div>

      <div id="right-navbar" class="flex">
        <a href="#"><img src="https://res.cloudinary.com/prometheusapi/image/upload/v1638303419/nav1_ngreqx.png" alt="" /></a>
        <a href="#"><img src="https://res.cloudinary.com/prometheusapi/image/upload/v1638303419/nav2_zhlzur.png" alt="" /></a>
        <a href="#"><img src="https://res.cloudinary.com/prometheusapi/image/upload/v1638303419/nav4_oc1mlz.png" alt="" /></a>
        <a href="#"><img src="https://res.cloudinary.com/prometheusapi/image/upload/v1638303419/nav3_ayeunv.png" alt="" /></a>
      </div>
    </nav>
    {% for data in items %}
    <main class="d-flex space-between">
      <div class="main-fc d-flex flex-column">
        <div class="container">
          <div class="main-video">
            <iframe
              width="853"
              height="480"
              src="https://www.youtube.com/embed/{{videoid}}"
              allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
              allowfullscreen
            ></iframe>
            <br>
            <br>
            <h3 class="video-title">
              {{data.snippet.title}}
            </h3>
            <div id="video-below" class="flex flex-horizontal">
              <div class="video-views">
                <p class="smaller-gray">{{data.statistics.viewCount|intcomma}} views • {{data.snippet.publishedAt|slice:"10"}}</p>
              </div>
              <div class="flex">
                <button>
                  <i class="fa fa-thumbs-up icon-spacing" aria-hidden="true">
                    {{data.statistics.likeCount|intcomma}}</i
                  >
                </button>

                <button>
                  <i class="fa fa-thumbs-down icon-spacing" aria-hidden="true">
                    {{data.statistics.dislikeCount|intcomma}}</i
                  >
                </button>
                <button><i class="fa fa-share icon-spacing">SHARE</i></button>
                <button>
                  <i class="fa fa-save icon-spacing" aria-hidden="true">
                    SAVE</i
                  >
                </button>
                <button>
                  <i
                    class="fa fa-ellipsis-h icon-spacing"
                    aria-hidden="true"
                  ></i>
                </button>
              </div>
            </div>
            <hr />
            <br />
          </div>

          <div class="page-profile">
            <div class="user-description">
              <button class="subscribe-button">ALIENX</button>
            </div>
            <h3>
              {{data.snippet.channelTitle}}
            </h3>
            <p>
              {{data.snippet.description}}
            </p>
          </div>

          <div class="video-comments">
            <div class="comment-section">
              <hr />
              <div class="sort-button">
                <h6>{{data.statistics.commentCount|intcomma}} Comments</h6>
                <div>
                  {% endfor %}
                </div>
              </div>
              <i class="fa fa-sort-amount-down gray" aria-hidden="true"></i>
            </div>
            <hr />
            
          </div>
        </div>
      </div>
      
    </main>
  </body>
</html>
```

### Template Explanation.

In the code above, we start by importing a very important package called [Django Humanize](https://docs.djangoproject.com/en/3.2/ref/contrib/humanize/). This package is useful for adding a “human touch” to data, in our case, changing the data pulled from youtube. The data we want to "humanize" in this case is the counts. You will notice from the API request above, the counts do not have commas, we then add the code below to format the data into commas.

```
<i class="fa fa-thumbs-up icon-spacing" aria-hidden="true">
                    {{data.statistics.likeCount|intcomma}}
</i>
```

## Testing our API

Make necessary migrations and then run the server. Head over to a youtube video of your choice and remove the HTTPS to HTTP. Remove [www.youtube.com](www.youtube.com) to point to your localhost and you should end up with a URL that resembles:

![fff.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638370515462/FsAQ4SwSOC.png)

If you have any questions, feel free to ask or test my API. Hit a thumbs down, sorry, thumbs up, if you enjoyed this post.