2

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)

Clary
  • 49
  • 6
  • 1
    On this line: `if 'rating' in newResponse and (newResponse['rating'] == 'like'...`, it would be possible to print the rating of the video? - just to make sure the videos are all retrieved videos from your first/old account were retrieved? - also, it would be possible to distribute your code in functions? – Marco Aurelio Fernandez Reyes Mar 09 '23 at 13:41
  • If the problem is *not* on the OAuth 2.0 - you *might* omit such code in your question and focus only on the looping and rating the videos. – Marco Aurelio Fernandez Reyes Mar 09 '23 at 13:43
  • @MarcoAurelioFernandezReyes I reorganized the code. Put them all into functions. Also, not sure how to print the rating of the video. I tried doing print(newResponse['rating']) and it didn't work. That's the same condition in my if statement. Starting to think that isn't the write syntax – Clary Mar 10 '23 at 02:16
  • 2
    Please don't put "Solved" in the title. Instead, post an answer (yes, you can [answer your own question](/help/self-answer)), then after 48 hours, you can click the checkmark to [accept it](/help/accepted-answer), which basically marks the issue as solved. That said, this might be effectively a duplicate of this question, though I'm not an expert in this field: [How to limit rate of requests to web services in Python?](/q/401215/4518341) – wjandrea Mar 10 '23 at 04:01
  • 1
    @wjandrea Thanks for the tips. I'll update the post – Clary Mar 10 '23 at 04:10
  • @MarcoAurelioFernandezReyes Hey I am having a new problem that you actually answered on another post. I am using a condition that if the video returns a 'like' or 'unspecified' response when I call getRating() to not like the video because that it means it is already liked. I thought 'unspecified' would mean that the video ratings are disabled but I am still getting an error that says I can't like certain videos because rating is disabled. You suggested to do 'none' and 'unspecified' but wouldn't that just skip any video and not actually like them since 'none' means never rated yet? – Clary Mar 10 '23 at 23:55
  • @Clary please ,post a new question and I'll give it a try. Thanks. – Marco Aurelio Fernandez Reyes Mar 13 '23 at 20:12

1 Answers1

2

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.

Clary
  • 49
  • 6