Search code examples
pythongoogle-apiyoutube-apigoogle-api-python-client

YouTube API videos.rate method only rates some videos


Code summary: Script that uses YouTube API and implements OAuth 2.0 to get access to one account, retrieve its liked videos, and get access to another account and use that list of liked videos to like them on the new account. It may seem like a weird project but I just started using Python this week and this is my first project and it seemed easy enough (I was wrong)

This is the relevant part of my code:

def likeVideos(videos,youtubeObject):

    youtube = youtubeObject
    videos = videos
    numofVideosLiked = 0
    numLikedVideos = 0

    for video in videos:
        numLikedVideos += 1

    for video in videos:
        newRequest = youtube.videos().getRating(
            id=[video['id']]
        )

        newResponse = newRequest.execute()

        if 'rating' in newResponse and (newResponse['rating'] == 'like' or newResponse['rating'] == 'unspecified'):
            print(f"Already liked '{video['snippet']['title']}' or something went wrong...")
        else:
            likeRequest = youtube.videos().rate(
                id=video['id'],
                rating='like'
            ).execute()

            numofVideosLiked += 1
            print(f"Liking {video['snippet']['title']}...{numofVideosLiked}/{numLikedVideos}")

The output in the terminal indicated that I successfully liked my first 194 videos from the parent account before it broke down:

Liking coding in c until my program is unsafe...191/354
Liking OVERDOSING ON TF2!!!...192/354
Liking JavaScript Fighting Game Tutorial with HTML Canvas...193/354
Liking DIY PCBs At Home (Single Sided Presensitized)...194/354

However, the child account only had 30 videos actually liked, and they were done in random intervals of 5. (To be specific, videos 1-5, 31-35, 68-72, 106-110, etc were actually liked on the second account) My understanding is that reading from lists is in groups of 5 so you need to increase the maxResults parameter like I did here when retrieving the list initially:

while True:
    # Make API request
    request = youtube.videos().list(
        part='snippet',
        myRating='like',
        maxResults=50,
        pageToken = next_page_token
    )

    response = request.execute()

    # Add videos to list
    videos += response['items']

    next_page_token = response.get('nextPageToken')
    if not next_page_token:
        break

But I am not sure what to do when actually calling the rate() function to like the videos because its only parameters are id and rating.

I believe this code block is the main problem:

if 'rating' in newResponse and (newResponse['rating'] == 'like' or newResponse['rating'] == 'unspecified'):
            print(f"Already liked '{video['snippet']['title']}' or something went wrong...")
        else:
            likeRequest = youtube.videos().rate(
                id=video['id'],
                rating='like'
            ).execute()

The if statement never catches already-liked videos when I run the script multiple times after some videos were already liked and the method of liking the videos doesn't seem to be accurate.

  • Is it possible Youtube limits how fast you can like videos? Is there a workaround?
  • What is a better way to check if the videos are already liked other than this code snippet?: if 'rating' in newResponse and (newResponse['rating'] == 'like' or newResponse['rating'] == 'unspecified'):

Here is the full source code only if it's necessary to help with my question:

from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import pickle
import os

# Make API request and build Youtube object
def getYoutubeObject(client_secret, path_to_pickled_file):
    
    credentials = None
    scopes = ["https://www.googleapis.com/auth/youtube.force-ssl",
          'https://www.googleapis.com/auth/youtube',
          'https://www.googleapis.com/auth/youtube.readonly',
          'https://www.googleapis.com/auth/youtube.upload',
          'https://www.googleapis.com/auth/youtubepartner',
          'https://www.googleapis.com/auth/youtubepartner-channel-audit']
    
    # path_to_pickled_file file stores user credentials
    if os.path.exists(path_to_pickled_file):
        print("Loading credentials of account from file...")
        with open(path_to_pickled_file, 'rb') as token:
            credentials = pickle.load(token)

    # If there are no valid credentials, refresh token or log in
    if not credentials or not credentials.valid:
        if credentials and credentials.expired and credentials.refresh_token:
            print("Refreshing access token for account...")
            credentials.refresh(Request())
        else:
            print("Fetching new tokens for account...")
            flow = InstalledAppFlow.from_client_secrets_file(
                client_secret, 
                scopes=scopes
            )
            flow.run_local_server(
                port=8080, 
                prompt="consent", 
                authorization_prompt_message=''
                )
            credentials = flow.credentials

            # Save credentials for next use
            with open(path_to_pickled_file, 'wb') as f:
                print("Saving credentials of account for future use...")
                pickle.dump(credentials, f)

    api_name = 'youtube'
    api_version = 'v3'

    youtube = build(api_name, api_version, credentials=credentials)

    return youtube

# Return list of liked videos
def getLikedVideos(youtubeObject):

    youtube = youtubeObject
    next_page_token = None
    videos = []
    # fields = "id, snippet(title)"

    while True:
        # Make API request
        request = youtube.videos().list(
            part='snippet',
            myRating='like',
            maxResults=50,
            pageToken = next_page_token
        )

        response = request.execute()

        # Add videos to list
        videos += response['items']

        next_page_token = response.get('nextPageToken')
        if not next_page_token:
            break

    numLikedVideos = 0

    for video in videos:
        numLikedVideos += 1

    print(f"You have {numLikedVideos} likes videos!")
    input("Press Enter to continue...")

    return videos

def likeVideos(videos,youtubeObject):

    youtube = youtubeObject
    videos = videos
    numofVideosLiked = 0
    numLikedVideos = 0

    for video in videos:
        numLikedVideos += 1

    for video in videos:
        newRequest = youtube.videos().getRating(
            id=[video['id']]
        )

        newResponse = newRequest.execute()

        if 'rating' in newResponse and (newResponse['rating'] == 'like' or newResponse['rating'] == 'unspecified'):
            print(f"Already liked '{video['snippet']['title']}' or something went wrong...")
        else:
            likeRequest = youtube.videos().rate(
                id=video['id'],
                rating='like'
            ).execute()

            numofVideosLiked += 1
            print(f"Liking {video['snippet']['title']}...{numofVideosLiked}/{numLikedVideos}")

def main():
    client_secret = 'client_secret.json'
    path_to_pickled_file = 'token.pickle'
    path_to_second_pickled_file = 'secondToken.pickle'

    input("Press Enter to retrieve first Youtube object...")
    youtube = getYoutubeObject(client_secret, path_to_pickled_file)

    input("Press Enter to get liked videos...")
    videos = getLikedVideos(youtube)

    input("Press Enter to retrieve second Youtube object...")
    newYtObj = getYoutubeObject(client_secret, path_to_second_pickled_file)

    input("Press Enter to like videos...")
    likeVideos(videos, newYtObj)

main()

I tried liking all videos within a list (350+ items) using the videos.rate method. But, only 30 ended up actually being rated (In intervals of 5 chronologically from my liked video history. The first 5 were liked, then 25 videos later it worked again and liked 5 more, etc)


Solution

  • Using import time, you can include time.sleep(time_interval_in_seconds) like such:

    def likeVideos(videos,youtubeObject):
    
        youtube = youtubeObject
        videos = videos
        numofVideosLiked = 0
        numLikedVideos = 0
    
        for video in videos:
            numLikedVideos += 1
    
        for video in videos:
            newRequest = youtube.videos().getRating(
                id=[video['id']]
            )
    
            newResponse = newRequest.execute()
    
            if 'rating' in newResponse and (newResponse['rating'] == 'like' or newResponse['rating'] == 'unspecified'):
                print(f"Already liked '{video['snippet']['title']}' or something went wrong...")
            else:
                likeRequest = youtube.videos().rate(
                    id=video['id'],
                    rating='like'
                ).execute()
    
                numofVideosLiked += 1
                print(f"Liking {video['snippet']['title']}...{numofVideosLiked}/{numLikedVideos}")
                time.sleep(1.5)
    

    The issue is not the code. The issue is the limit the YouTube API has on how fast you can request certain data.