You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Python调用YouTube API单次请求批量添加视频至播放列表?

YouTube与Spotify播放列表互转工具的API配额优化问题

我正在开发一款YouTube与Spotify播放列表互转工具,但遇到YouTube API配额限制问题:每添加一个视频到播放列表需消耗50配额点(每日总配额为10000点),这严重限制了可处理的曲目数量。请问是否有办法将列表中的所有曲目通过单次请求添加到播放列表中?

原实现代码

import os
import googleapiclient.discovery
import googleapiclient.errors
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
from google_auth_oauthlib.flow import InstalledAppFlow
from secret.credentials import Credentials
from controller import getter
from gui.colors import colored_text, ConsoleColors

# 定义权限范围
SCOPES = ["https://www.googleapis.com/auth/youtube.force-ssl"]

def extract_spotify_playlist_name(spotify_playlist_url):
    # 初始化Spotipy客户端
    client_credentials_manager = SpotifyClientCredentials(Credentials().get_id(), Credentials().get_secret())
    sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)

    # 从URL中提取播放列表ID
    playlist_id = spotify_playlist_url.split('/')[-1]

    # 获取播放列表详情
    playlist = sp.playlist(playlist_id)

    # 提取播放列表名称
    playlist_name = playlist['name']

    return playlist_name

def create_youtube_playlist(spotify_playlist_url, youtube_playlist_name):
    # 重新定义权限范围
    SCOPES = ["https://www.googleapis.com/auth/youtube"]

    # 加载JSON格式的凭据
    creds = None
    if os.path.exists('ytb_credentials/client_secret.json'):
        creds = InstalledAppFlow.from_client_secrets_file(
            'ytb_credentials/client_secret.json', SCOPES).run_local_server(port=0)

    # 使用凭据构建YouTube API服务
    youtube = googleapiclient.discovery.build('youtube', 'v3', credentials=creds)

    # 创建YouTube播放列表
    try:
        request = youtube.playlists().insert(
            part="snippet,status",
            body={
                "snippet": {
                    "title": youtube_playlist_name,
                    "description": f"This playlist was created from {youtube_playlist_name} using BridgeBeats.",
                    "tags": ["BridgeBeats"]
                },
                "status": {
                    "privacyStatus": "public"  # 可改为"private"创建私有播放列表
                }
            }
        )
        response = request.execute()
        playlist_id = response["id"]
        print(f"{colored_text('Playlist created successfully!', ConsoleColors.BACKGROUND_GREEN)} Playlist ID:", playlist_id)

        # 获取Spotify播放列表中的曲目
        spotify_tracks = getter.get_spotify_tracks(spotify_playlist_url)

        # 将曲目添加到YouTube播放列表
        for track in spotify_tracks:
            try:
                # 在YouTube上搜索曲目
                search_response = youtube.search().list(
                    q=f"{track['artist']} - {track['name']}",
                    part="id",
                    maxResults=1
                ).execute()

                # 将搜索结果中的第一个视频添加到播放列表
                video_id = search_response["items"][0]["id"]["videoId"]
                youtube.playlistItems().insert(
                    part="snippet",
                    body={
                        "snippet": {
                            "playlistId": playlist_id,
                            "position": 0,
                            "resourceId": {
                                "kind": "youtube#video",
                                "videoId": video_id
                            }
                        }
                    }
                ).execute()

                print(f"Added {colored_text(f'{track["artist"]} - {track["name"]}', ConsoleColors.GREEN)} to the playlist.")
            except Exception as e:
                print(f"{colored_text(f'Error adding {track["artist"]} - {track["name"]} to the playlist:', ConsoleColors.RED)}", e)

    except googleapiclient.errors.HttpError as e:
        print(f"{colored_text('An error occurred while creating the playlist:', ConsoleColors.RED)}", e)
        return

GPT提供的批量方案(无法正常运行)

def create_youtube_playlist(spotify_playlist_url, youtube_playlist_name):
    # 加载JSON格式的凭据
    creds = None
    if os.path.exists('ytb_credentials/client_secret.json'):
        creds = InstalledAppFlow.from_client_secrets_file(
            'ytb_credentials/client_secret.json', SCOPES).run_local_server(port=0)

    # 构建YouTube API服务
    youtube = googleapiclient.discovery.build('youtube', 'v3', credentials=creds)

    # 创建YouTube播放列表
    try:
        request = youtube.playlists().insert(
            part="snippet,status",
            body={
                "snippet": {
                    "title": youtube_playlist_name,
                    "description": f"This playlist was created from {youtube_playlist_name} using BridgeBeats.",
                    "tags": ["BridgeBeats"]
                },
                "status": {
                    "privacyStatus": "public"  # 可改为"private"创建私有播放列表
                }
            }
        )
        response = request.execute()
        playlist_id = response["id"]
        print(f"{colored_text('Playlist created successfully!', ConsoleColors.BACKGROUND_GREEN)} Playlist ID:", playlist_id)

        # 获取Spotify播放列表中的曲目
        spotify_tracks = getter.get_spotify_tracks(spotify_playlist_url)

        # 准备批量请求
        batch = youtube.new_batch_http_request(callback=batch_callback)
        
        # 将曲目添加到YouTube播放列表
        for track in spotify_tracks:
            try:
                # 在YouTube上搜索曲目
                search_response = youtube.search().list(
                    q=f"{track['artist']} - {track['name']}",
                    part="id",
                    maxResults=1
                ).execute()

                # 将搜索结果中的第一个视频添加到播放列表
                video_id = search_response["items"][0]["id"]["videoId"]
                batch.add(youtube.playlistItems().insert(
                    part="snippet",
                    body={
                        "snippet": {
                            "playlistId": playlist_id,
                            "position": 0,
                            "resourceId": {
                                "kind": "youtube#video",
                                "videoId": video_id
                            }
                        }
                    }
                ))
                
                print(f"Added {colored_text(f'{track["artist"]} - {track["name"]}', ConsoleColors.GREEN)} to the batch.")
            except Exception as e:
                print(f"{colored_text(f'Error adding {track["artist"]} - {track["name"]} to the batch:', ConsoleColors.RED)}", e)

        # 执行批量请求
        batch.execute()

    except googleapiclient.errors.HttpError as e:
        print(f"{colored_text('An error occurred while creating the playlist:', ConsoleColors.RED)}", e)
        return

def batch_callback(request_id, response, exception):
    if exception is not None:
        print(f"Request {request_id} failed: {exception}")
    else:
        print(f"Request {request_id} succeeded!")

解决方案说明与修复后的代码

首先明确:YouTube Data API 没有提供单次请求添加多个视频到播放列表的接口,批量请求(Batch Requests)只是将多个playlistItems.insert请求打包成一个HTTP请求发送,每个请求仍会单独消耗50配额点,但可以减少HTTP连接开销,提升处理速度。

GPT提供的方案无法运行的常见原因包括:权限范围定义冲突、colored_text调用语法错误、批量请求大小超限、回调函数作用域问题。以下是修复后的可运行代码:

import os
import googleapiclient.discovery
import googleapiclient.errors
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
from google_auth_oauthlib.flow import InstalledAppFlow
from secret.credentials import Credentials
from controller import getter
from gui.colors import colored_text, ConsoleColors

# 全局定义权限范围
SCOPES = ["https://www.googleapis.com/auth/youtube"]

def extract_spotify_playlist_name(spotify_playlist_url):
    client_credentials_manager = SpotifyClientCredentials(Credentials().get_id(), Credentials().get_secret())
    sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager)
    playlist_id = spotify_playlist_url.split('/')[-1]
    playlist = sp.playlist(playlist_id)
    return playlist['name']

def batch_callback(request_id, response, exception):
    """批量请求回调函数,处理单个请求结果"""
    if exception is not None:
        print(f"{colored_text(f'Request {request_id} failed:', ConsoleColors.RED)} {exception}")
    else:
        print(f"{colored_text(f'Request {request_id} succeeded!', ConsoleColors.GREEN)}")

def create_youtube_playlist(spotify_playlist_url, youtube_playlist_name):
    creds = None
    if os.path.exists('ytb_credentials/client_secret.json'):
        creds = InstalledAppFlow.from_client_secrets_file(
            'ytb_credentials/client_secret.json', SCOPES).run_local_server(port=0)

    youtube = googleapiclient.discovery.build('youtube', 'v3', credentials=creds)

    try:
        # 创建YouTube播放列表
        request = youtube.playlists().insert(
            part="snippet,status",
            body={
                "snippet": {
                    "title": youtube_playlist_name,
                    "description": f"This playlist was created from Spotify using BridgeBeats.",
                    "tags": ["BridgeBeats"]
                },
                "status": {
                    "privacyStatus": "public"
                }
            }
        )
        response = request.execute()
        playlist_id = response["id"]
        print(f"{colored_text('Playlist created successfully!', ConsoleColors.BACKGROUND_GREEN)} Playlist ID: {playlist_id}")

        spotify_tracks = getter.get_spotify_tracks(spotify_playlist_url)
        batch = youtube.new_batch_http_request(callback=batch_callback)
        request_count = 0
        max_batch_size = 50  # 限制单批请求数,避免触发API限流

        for track in spotify_tracks:
            try:
                # 限定搜索类型为视频,避免返回频道/播放列表
                search_response = youtube.search().list(
                    q=f"{track['artist']} - {track['name']}",
                    part="id",
                    maxResults=1,
                    type="video"
                ).execute()

                if not search_response["items"]:
                    print(f"{colored_text(f'No video found for {track["artist"]} - {track["name"]}', ConsoleColors.YELLOW)}")
                    continue

                video_id = search_response["items"][0]["id"]["videoId"]
                # 添加插入请求到批量任务,用曲目信息作为请求ID便于定位问题
                batch.add(
                    youtube.playlistItems().insert(
                        part="snippet",
                        body={
                            "snippet": {
                                "playlistId": playlist_id,
                                "position": 0,
                                "resourceId": {
                                    "kind": "youtube#video",
                                    "videoId": video_id
                                }
                            }
                        }
                    ),
                    request_id=f"{track['artist']}-{track['name']}"
                )
                request_count += 1

                # 达到批量上限时执行请求并重置批量任务
                if request_count >= max_batch_size:
                    batch.execute()
                    batch = youtube.new_batch_http_request(callback=batch_callback)
                    request_count = 0
                    print(f"{colored_text('Batch executed, continuing...', ConsoleColors.BLUE)}")

                print(f"{colored_text(f'Added {track["artist"]} - {track["name"]} to batch', ConsoleColors.GREEN)}")
            except Exception as e:
                print(f"{colored_text(f'Error processing {track["artist"]} - {track["name"]}:', ConsoleColors.RED)} {e}")

        # 执行剩余的批量请求
        if request_count > 0:
            batch.execute()
            print(f"{colored_text('Final batch executed!', ConsoleColors.BLUE)}")

    except googleapiclient.errors.HttpError as e:
        print(f"{colored_text('An error occurred while creating the playlist:', ConsoleColors.RED)} {e}")
        return

额外配额优化建议

  1. 缓存搜索结果:将已搜索过的曲目-视频ID映射存储到本地,避免重复搜索(单次search.list请求消耗100配额点,比添加操作更高)
  2. 使用ISRC码搜索:如果能获取到Spotify曲目的ISRC码,直接用ISRC搜索YouTube视频,提升匹配准确率并减少无效搜索
  3. 分时段处理:若播放列表曲目超过200首(10000/50),拆分到不同日期处理,避免耗尽当日配额

内容的提问来源于stack exchange,提问作者lilrau

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 21:22:03