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

通过OAuth授权用户获取YouTube频道及视频的Laravel技术问题

解决Laravel中通过Google Socialite获取YouTube频道及视频的问题

看起来你已经走了正确的第一步——用Laravel Socialite配置了youtube.readonly权限并获取了访问令牌,但在后续调用YouTube API时遇到了问题。我来帮你梳理常见的问题点和完整的实现方案:

1. 先补全授权请求:确保能获取Refresh Token

Google的OAuth2访问令牌默认只有1小时有效期,要实现自动刷新令牌,必须在授权时请求offline_access权限(Socialite不会默认添加)。修改你的loginGoogle方法:

public function loginGoogle() { 
    return Socialite::driver('google')
        ->setScopes(['https://www.googleapis.com/auth/youtube.readonly'])
        // 添加这两个参数,确保能拿到refresh_token
        ->with(['access_type' => 'offline', 'prompt' => 'consent'])
        ->redirect(); 
}
  • access_type=offline:告诉Google我们需要离线访问权限,会返回refresh_token
  • prompt=consent:强制用户重新授权(即使之前授权过),确保每次都能拿到refresh_token(否则首次授权后后续可能不会返回)

2. 完善回调处理:保存完整的令牌信息

在回调方法中,除了access_token,还要保存refresh_token和过期时间,这样后续才能自动刷新令牌:

public function handleGoogleCallback()
{
    try {
        $user = Socialite::driver('google')->user();
        
        // 将令牌信息存入会话(如果需要持久化可以存数据库)
        session([
            'google_access_token' => $user->token,
            'google_refresh_token' => $user->refreshToken,
            'google_token_expires_at' => now()->addSeconds($user->expiresIn)
        ]);
        
        return redirect()->route('youtube.fetch-content'); // 跳转到获取内容的页面/接口
    } catch (\Exception $e) {
        // 处理授权失败的情况
        return redirect()->route('login')->with('error', 'Google授权失败:'.$e->getMessage());
    }
}

3. 调用YouTube API获取频道和视频

推荐使用Google官方的PHP客户端来处理API调用,它会自动处理令牌刷新。首先安装依赖:

composer require google/apiclient:^2.0

然后创建一个方法来获取频道和视频:

use Google\Client;
use Google\Service\YouTube;
use Illuminate\Http\Request;

public function fetchYouTubeContent(Request $request)
{
    // 初始化Google客户端
    $client = new Client();
    $client->setClientId(config('services.google.client_id'));
    $client->setClientSecret(config('services.google.client_secret'));
    $client->setRedirectUri(config('services.google.redirect'));
    
    // 从会话获取令牌信息
    $accessToken = session('google_access_token');
    $refreshToken = session('google_refresh_token');
    $expiresAt = session('google_token_expires_at');
    
    // 设置令牌到客户端
    $client->setAccessToken([
        'access_token' => $accessToken,
        'refresh_token' => $refreshToken,
        'expires_in' => $expiresAt->diffInSeconds(now()),
        'created' => $expiresAt->subSeconds($expiresAt->diffInSeconds(now()))->timestamp
    ]);
    
    // 自动刷新过期的令牌
    if ($client->isAccessTokenExpired()) {
        $newTokenData = $client->fetchAccessTokenWithRefreshToken($refreshToken);
        
        // 更新会话中的令牌信息
        session([
            'google_access_token' => $newTokenData['access_token'],
            'google_token_expires_at' => now()->addSeconds($newTokenData['expires_in'])
        ]);
        $client->setAccessToken($newTokenData);
    }
    
    // 初始化YouTube服务
    $youtube = new YouTube($client);
    
    try {
        // 1. 获取当前用户的所有频道
        $channelsResponse = $youtube->channels->listChannels('snippet,contentDetails', [
            'mine' => true // 表示获取当前授权用户的频道
        ]);
        
        $channels = $channelsResponse->getItems();
        if (empty($channels)) {
            return response()->json(['message' => '该用户未创建任何YouTube频道']);
        }
        
        // 2. 获取每个频道的所有视频
        $allVideos = [];
        foreach ($channels as $channel) {
            $channelTitle = $channel->getSnippet()->getTitle();
            // 获取频道的"上传"播放列表ID(所有视频都存在这个播放列表里)
            $uploadsPlaylistId = $channel->getContentDetails()->getRelatedPlaylists()->getUploads();
            
            // 获取播放列表中的视频(默认最多50条,需要分页加载更多)
            $playlistItems = [];
            $pageToken = null;
            
            do {
                $playlistResponse = $youtube->playlistItems->listPlaylistItems('snippet', [
                    'playlistId' => $uploadsPlaylistId,
                    'maxResults' => 50,
                    'pageToken' => $pageToken
                ]);
                
                $playlistItems = array_merge($playlistItems, $playlistResponse->getItems());
                $pageToken = $playlistResponse->getNextPageToken();
            } while ($pageToken);
            
            $allVideos[$channelTitle] = $playlistItems;
        }
        
        // 返回频道和视频数据
        return response()->json([
            'channels' => $channels,
            'videos' => $allVideos
        ]);
        
    } catch (\Google\Service\Exception $e) {
        // 处理YouTube API的错误(比如权限不足、配额超限)
        return response()->json(['error' => 'API调用失败:'.$e->getMessage()], 500);
    } catch (\Exception $e) {
        return response()->json(['error' => '系统错误:'.$e->getMessage()], 500);
    }
}

常见问题排查

  • 令牌过期/无效:确保你保存了refresh_token,并且在授权时添加了access_type=offline
  • 权限不足:确认scope是youtube.readonly,这个权限足够读取频道和视频信息
  • API配额超限:YouTube API有每日配额限制(默认10000单位),如果调用频繁会触发配额不足错误,此时需要等待配额重置或申请提高配额
  • 无频道返回:用户可能没有创建任何YouTube频道,这种情况要做友好提示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:57:20