Skip to content

Instantly share code, notes, and snippets.

@bulletinmybeard
Created March 18, 2024 08:29
Show Gist options
  • Save bulletinmybeard/574444e65a0eb5e4c6b388751e816ea8 to your computer and use it in GitHub Desktop.
Save bulletinmybeard/574444e65a0eb5e4c6b388751e816ea8 to your computer and use it in GitHub Desktop.
Export your YouTube liked and disliked videos (.csv)

Create a bash script with the code below as export-yt-liked-and-disliked-videos.sh, give it execution rights with chmod +x export-yt-liked-and-disliked-videos.sh, and run it with your Google access token like so:

./export-yt-liked-and-disliked-videos.sh ya29.a0Ad52N39hmTcEjI1QoL...

The Bash Script

#!/bin/bash

ratings=("like" "dislike")
auth_token="$1"
csv_file="youtube_api_export.csv"

declare -a existingIds

read_existing_ids() {
    if [ -f "$csv_file" ]; then
        IFS=$'\n' read -d '' -r -a existingIds < <(tail -n +2 "$csv_file" | cut -d ',' -f 1 | tr -d '"' && printf '\0')
    else
        echo "CSV file does not exist, creating a new one."
        echo "id,title" > "$csv_file"
    fi
    echo "Loaded ${#existingIds[@]} existing video IDs."
}

update_csv() {
    local items=$1

    item_count=$(echo "$items" | jq '.items | length')
    echo "Processing $item_count new items..."

    local skipped_count=0
    local added_count=0

    for ((i = 0; i < item_count; i++)); do
        local item
        item=$(echo "$items" | jq ".items[$i]")
        local id
        id=$(echo "$item" | jq -r '.id')
        local title
        title=$(echo "$item" | jq -r '.snippet.title' | sed 's/"/""/g')
        local csvLine
        csvLine="\"$id\",\"$title\""

        if [[ " ${existingIds[*]} " =~ ${id} ]]; then
            ((skipped_count++))
            echo "Skipping existing YouTube video ID: $id"
            continue
        fi

        echo "$csvLine" >> "$csv_file"
        ((added_count++))
    done

    echo "Added $added_count new items, skipped $skipped_count items."
}

if [ ! -f "$csv_file" ]; then
    echo "id,title" > "$csv_file"
fi

read_existing_ids

fetch_and_process_videos() {
    local rating=$1
    local nextPageToken=""
    local url="https://www.googleapis.com/youtube/v3/videos?myRating=$rating&maxResults=50&fields=nextPageToken%2CpageInfo%2Citems(id%2Csnippet(title))&part=snippet"

    while true; do
        local requestUrl="${url}"
        if [[ -n $nextPageToken ]]; then
            requestUrl="${url}&pageToken=${nextPageToken}"
        fi

        response=$(curl -s --request GET \
            --url "$requestUrl" \
            --header "Accept: application/json" \
            --header "Authorization: Bearer $auth_token" \
            --header "User-Agent: insomnia/8.6.1" | jq)

        update_csv "$response"

        nextPageToken=$(echo "$response" | jq -r '.nextPageToken')

        if [[ $nextPageToken == "null" ]] || [[ -z $nextPageToken ]]; then
            echo "No more pages to fetch for rating '$rating'."
            break
        fi

        sleep 1
    done
}

for rating in "${ratings[@]}"; do
    echo "Processing rating: $rating"
    fetch_and_process_videos "$rating"
done

echo "CSV file '$csv_file' update complete."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment