Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions youtube_video_language_bulk_updater/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
YouTube Video Language Bulk Updater

This script is designed to update the Default Language and Default Audio Language of YouTube videos in bulk. It retrieves a list of videos from a Google Spreadsheet and automatically updates the language settings for each video sequentially via the YouTube Data API.
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"source": [
"# Copyright 2025 Google LLC\n",
"\n",
"Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n",
"\n",
" https://www.apache.org/licenses/LICENSE-2.0\n",
"\n",
"Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License."
],
"metadata": {
"id": "RDvE9v4Z_mUI"
}
},
{
"cell_type": "markdown",
"source": [
"#About this Colab\n",
"\n",
"This script is designed to update the Default Language and Default Audio Language of YouTube videos in bulk. It retrieves a list of videos from a Google Spreadsheet and automatically updates the language settings for each video sequentially via the YouTube Data API."
],
"metadata": {
"id": "5Ro6BPq1uHfW"
}
},
{
"cell_type": "markdown",
"source": [
"\n",
"###**Preparation Steps**\n",
"\n",
"**1. Create a Google Gloud Project**\n",
" * Create a Google Cloud project if you don't already have one. Follow [instructions](https://developers.google.com/workspace/guides/create-project) on how to set it up.\n",
"\n",
"**2. Enable APIs**\n",
"\n",
" * Enable the [YouTube Data API v3](https://console.cloud.google.com/marketplace/product/google/youtube.googleapis.com)\n",
" * Enable the [Google Sheets API](https://console.cloud.google.com/marketplace/product/google/sheets.googleapis.com)\n",
"\n",
"\n",
"**3 Create a Service account (CMS Level)**\n",
"\n",
"* Create a service account in the 'Service Accounts' section of 'IAM & Admin' and a service account key for it using the [following tutorial](https://cloud.google.com/iam/docs/keys-create-delete).\n",
"* Get the email of the created Service Account. It ends with iam.gserviceaccount.com.\n",
"* Then generate a JSON key file for this service account using the [following tutorial](https://developers.google.com/workspace/guides/create-credentials#create_credentials_for_a_service_account). It automatically downloads the key. (Section: Create Credentials for service account)\n",
"* Add your service account email as 'Video manager' to your CMS using the [following tutorial](https://support.google.com/youtube/answer/4524878). It is automaticall enabled without the confirmation of the invitation.\n",
"* Add this service account to the Google Spreadsheet with the target video list as an editor.\n",
"\n",
"**4. Prepare a Target Video List**\n",
"\n",
"Paste the list of target videos for language change under the external_video_id column in the spreadsheet."
],
"metadata": {
"id": "o_R1gY_rmgdH"
}
},
{
"cell_type": "markdown",
"source": [
"# Input Variables"
],
"metadata": {
"id": "NyTWB3g6649a"
}
},
{
"cell_type": "code",
"source": [
"# Spreadsheet URL which the metadata will be stored\n",
"SPREADSHEET_URL = '' # @param {type:\"string\"}\n",
"\n",
"# Spreadsheet URL which the metadata will be stored\n",
"SHEET_NAME = 'Sheet1' # @param {type:\"string\"}\n",
"\n",
"# TARGET Audio Language to be changed. i.e. ko\n",
"TARGET_AUDIO_LANGUAGE = '' # @param {type:\"string\"}"
],
"metadata": {
"id": "i-I_494rDllm"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"#Authentication and Library Setup"
],
"metadata": {
"id": "Xd82yM3-6_SP"
}
},
{
"cell_type": "code",
"source": [
"from google.oauth2 import service_account\n",
"from google.colab import files\n",
"import json\n",
"from googleapiclient.discovery import build\n",
"\n",
"# scope, name, version for YouTube Data API and Google Sheet API\n",
"YOUTUBE_DATA_API_SCOPES = [\"https://www.googleapis.com/auth/youtube.force-ssl\"]\n",
"GOOGLE_SHEET_API_SCOPE = [\"https://www.googleapis.com/auth/spreadsheets\"]\n",
"API_SERVICE_NAME = \"youtube\"\n",
"API_VERSION = \"v3\"\n",
"\n",
"# Load json file\n",
"def service_account_loads():\n",
" service_account_upload = files.upload()\n",
" service_account_json = json.loads(\n",
" next(iter(service_account_upload.values()))\n",
" )\n",
" return service_account_json\n",
"\n",
"service_account_json = service_account_loads()\n",
"\n",
"# Build YouTube Data API Service\n",
"youtube_data_credentials = service_account.Credentials.from_service_account_info(\n",
" service_account_json, scopes=YOUTUBE_DATA_API_SCOPES)\n",
"youtube_service = build(\n",
" API_SERVICE_NAME,\n",
" API_VERSION,\n",
" credentials=youtube_data_credentials,\n",
" )\n",
"\n",
"# Get Credential For Google Spreadsheet\n",
"spreadsheet_credentials = service_account.Credentials.from_service_account_info(\n",
" service_account_json, scopes=GOOGLE_SHEET_API_SCOPE)"
],
"metadata": {
"id": "8q4U5ZRF9ASr"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"# Main Process\n",
"\n",
"1. Open the target spreadsheet\n",
"2. Retrieve the individual video ids\n",
"3. Update the defaultLanguage and defaultAudioLanguage of the video\n",
"4. Display the result"
],
"metadata": {
"id": "Q0vm9kE17W9I"
}
},
{
"cell_type": "code",
"source": [
"import gspread\n",
"import pandas as pd\n",
"import googleapiclient.errors\n",
"\n",
"# open a spreadsheet with the target video list\n",
"def open_spreadsheet():\n",
" print('Opening the spreadsheet.')\n",
" gc = gspread.authorize(spreadsheet_credentials)\n",
"\n",
" try:\n",
" worksheet = gc.open_by_url(SPREADSHEET_URL).worksheet(SHEET_NAME)\n",
" all_data = worksheet.get_all_records()\n",
" if not all_data:\n",
" print('No data found in the spreadsheet.')\n",
" return None\n",
" df = pd.DataFrame(all_data)\n",
" return df\n",
" except Exception as e:\n",
" print(f\"An unexpected error occurred while reading the file: {e}\")\n",
" return None\n",
"\n",
"\n",
"# Send a request to update a video language\n",
"def request_video_language_update(video_resource_body):\n",
" try:\n",
" update_request = youtube_service.videos().update(\n",
" part=\"snippet\",\n",
" body=video_resource_body\n",
" )\n",
" update_request.execute()\n",
"\n",
" print(f\"SUCCESS: Video ID {video_id} updated (Default='{TARGET_AUDIO_LANGUAGE}', Audio='{TARGET_AUDIO_LANGUAGE}'.\")\n",
" return True\n",
"\n",
" except googleapiclient.errors.HttpError as e:\n",
" print(f\"FAILED: Error updating video (Code: {e.resp.status}).\")\n",
" if e.resp.status == 403:\n",
" print(\"Reason: Permission denied. Check video ownership or API scope.\")\n",
" return None\n",
" except Exception as e:\n",
" print(f\"An unexpected error occurred: {e}\")\n",
" return None\n",
"\n",
"\n",
"# Update defaultLanguage and defaultAudioLanguage of a video\n",
"def update_video_language(video_id):\n",
" # Get current video snippet (required to include mandatory fields in update)\n",
" try:\n",
" get_request = youtube_service.videos().list(part=\"snippet\", id=video_id)\n",
" get_response = get_request.execute()\n",
"\n",
" if not get_response.get(\"items\"):\n",
" print(f\"Video ID '{video_id}' not found.\")\n",
" return None\n",
"\n",
" current_snippet = get_response[\"items\"][0][\"snippet\"]\n",
" if current_snippet['defaultLanguage'] == TARGET_AUDIO_LANGUAGE and current_snippet['defaultAudioLanguage'] == TARGET_AUDIO_LANGUAGE:\n",
" print(f\"Video ID '{video_id}' is already in Korean.\")\n",
" return None\n",
"\n",
" # Construct and execute the update request\n",
" video_resource_body = {\n",
" \"id\": video_id,\n",
" \"snippet\": {\n",
" # MANDATORY fields to include:\n",
" \"title\": current_snippet[\"title\"],\n",
" \"description\": current_snippet[\"description\"],\n",
" \"categoryId\": current_snippet[\"categoryId\"],\n",
" # Language fields to update:\n",
" \"defaultLanguage\": TARGET_AUDIO_LANGUAGE,\n",
" \"defaultAudioLanguage\": TARGET_AUDIO_LANGUAGE\n",
" }\n",
" }\n",
"\n",
" result = request_video_language_update(video_resource_body)\n",
" return result\n",
"\n",
" except googleapiclient.errors.HttpError as e:\n",
" print(f\"FAILED: Error Getting video data (Code: {e.resp.status}).\")\n",
" if e.resp.status == 403:\n",
" print(\"Reason: Permission denied. Check video ownership or API scope.\")\n",
" return None\n",
" except Exception as e:\n",
" print(f\"An unexpected error occurred with Video ID '{video_id}' : {e}\")\n",
" return None\n",
"\n",
"\n",
"if __name__ == '__main__':\n",
"\n",
" count = 0\n",
"\n",
" # Open shpreadsheet\n",
" df = open_spreadsheet()\n",
"\n",
" # Load video list to memory\n",
" videos = df['external_video_id']\n",
" video_list = videos.tolist()\n",
" print(f\"Successfully loaded {len(video_list)} initial rows.\")\n",
"\n",
" # Retrieve the individual video ids and update the language\n",
" for video_id in video_list:\n",
" result = update_video_language(video_id)\n",
" if not result:\n",
" continue\n",
" else:\n",
" count += 1\n",
"\n",
" # display the count of successfully updated videos\n",
" print(f\"Total {count} videos were successfully updated.\")"
],
"metadata": {
"id": "x49YFCl1De6A"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"\n"
],
"metadata": {
"id": "Vocm8tuTAi-i"
},
"execution_count": null,
"outputs": []
}
]
}