configData = $configData; $this->client = new Google_Client(); $this->client->setClientId(env('SSM_YOUTUBE_CLIENT_ID')); $this->client->setClientSecret(env('SSM_YOUTUBE_CLIENT_SECRET')); $this->client->setRedirectUri(env('DIST_SITE_URL').'/dist/callback/youtube'); $this->client->setScopes([ Google_Service_YouTube::YOUTUBE_UPLOAD, Google_Service_YouTube::YOUTUBE, Google_Service_YouTube::YOUTUBE_FORCE_SSL, Google_Service_YouTube::YOUTUBE_READONLY, Google_Service_YouTube::YOUTUBEPARTNER, Google_Service_YouTube::YOUTUBEPARTNER_CHANNEL_AUDIT ]); $this->client->setAccessType('offline'); $this->client->setPrompt('consent'); $this->client->setDeveloperKey(env('SSM_YOUTUBE_DEV_KEY')); } public function login() { try { return ['status' => true, 'data' => ['url' => $this->client->createAuthUrl()]]; } catch (\Exception $e) { return ['status' => false, 'data' => 'Failed to generate login URL: '.$e->getMessage()]; } } public function loginCallback(Request $request) { try { $code = $request->input('code'); if (!$code) { return ['status' => false, 'data' => 'Authorization code not found']; } $token = $this->client->fetchAccessTokenWithAuthCode($code); if (isset($token['error'])) { return ['status' => false, 'data' => $token['error_description'] ?? 'Failed to exchange authorization code']; } $this->client->setAccessToken($token); $youtube = new Google_Service_YouTube($this->client); $channelResponse = $youtube->channels->listChannels('snippet', ['mine' => true]); return [ 'status' => true, 'data' => [ 'accessToken' => $token['access_token'], 'refreshToken' => $token['refresh_token'] ?? '', 'accessToken_expiresAt' => Carbon::now()->addSeconds($token['expires_in']), 'userName' => $channelResponse->items[0]->snippet->title ?? '', 'userId' => $channelResponse->items[0]->id ?? '', ] ]; } catch (\Exception $e) { return ['status' => false, 'data' => $e->getMessage()]; } } public function postVideo($message, $videoPath, $accessToken) { $ossPath = ltrim(parse_url($videoPath, PHP_URL_PATH), '/'); $fileSize = $this->getOssFileSize($ossPath); dd($fileSize); // 处理 OAuth 回调 $this->client->setAccessToken($accessToken); // 初始化 YouTube 服务 $youtube = new Google_Service_YouTube($this->client); try { $requestContent = []; // 定义视频元数据 $requestContent[] = $snippetData; $snippetData = [ 'title' => $message, 'description' => '', 'tags' => [], 'categoryId' => '22' // 人物与博客 ]; $snippet = new Google_Service_YouTube_VideoSnippet(); $snippet->setTitle($snippetData['title']); $snippet->setDescription($snippetData['description']); $snippet->setTags($snippetData['tags']);//'test', 'api' $snippet->setCategoryId($snippetData['categoryId']); // 人物与博客 // 设置视频状态 $status = new Google_Service_YouTube_VideoStatus(); $status->setPrivacyStatus('public'); // public, private, unlisted // 创建视频对象 $video = new Google_Service_YouTube_Video(); $video->setSnippet($snippet); $video->setStatus($status); // 上传视频 $this->client->setDefer(true); $insertRequest = $youtube->videos->insert('snippet,status', $video); $media = new Google_Http_MediaFileUpload( $this->client, $insertRequest, 'video/*', null, true, 2 * 1048576 // 分块大小 2MB ); $media->setFileSize($fileSize); // 替换为视频文件路径 $status = false; $handle = fopen($videoPath, 'rb'); while (!$status && !feof($handle)) { $chunk = fread($handle, 2 * 1048576); $status = $media->nextChunk($chunk); } fclose($handle); $this->client->setDefer(false); //echo "Video uploaded: " . $status['id']; $responseContent = [$status]; return ['status' => true, 'data' => [ 'responseIds' => [$status['id']], 'requestContent' => $requestContent, 'responseContent' => $responseContent, ]]; } catch (Google_Service_Exception $e) { return ['status' => false, 'data' => $e->getMessage()]; } catch (Google_Exception $e) { return ['status' => false, 'data' => $e->getMessage()]; } } public function getVideoCategories($regionCode = 'US') { try { $youtube = new Google_Service_YouTube($this->client); $response = $youtube->videoCategories->listVideoCategories('snippet', [ 'regionCode' => $regionCode ]); $categories = []; foreach ($response->items as $category) { if ($category->snippet->assignable) { $categories[$category->id] = $category->snippet->title; } } return [ 'status' => true, 'data' => $categories ]; } catch (\Google_Service_Exception $e) { $error = json_decode($e->getMessage(), true)['error']['message'] ?? $e->getMessage(); return ['status' => false, 'data' => $error]; } } public function postImage($message, $imagePaths, $accessToken) { return [ 'status' => false, 'error' => 'YouTube API does not support image-only posts' ]; } public function getComments($postId) { try { $youtube = new Google_Service_YouTube($this->client); $response = $youtube->commentThreads->listCommentThreads('snippet', [ 'videoId' => $postId, 'maxResults' => 100 ]); $comments = []; foreach ($response->items as $item) { $comment = $item->snippet->topLevelComment->snippet; $comments[] = [ 'id' => $item->id, 'text' => $comment->textDisplay, 'author' => $comment->authorDisplayName, 'createdAt' => $comment->publishedAt ]; } return ['status' => true, 'data' => $comments]; } catch (\Exception $e) { Log::error('YouTube get comments error: '.$e->getMessage()); return ['status' => false, 'error' => $e->getMessage()]; } } public function replyToComment($commentId) { $text = ''; try { $youtube = new Google_Service_YouTube($this->client); $commentSnippet = new \Google_Service_YouTube_CommentSnippet(); $commentSnippet->setTextOriginal($text); $commentSnippet->setParentId($commentId); $comment = new \Google_Service_YouTube_Comment(); $comment->setSnippet($commentSnippet); $response = $youtube->comments->insert('snippet', $comment); return [ 'status' => true, 'data' => [ 'commentId' => $response->getId(), 'text' => $text ] ]; } catch (\Exception $e) { Log::error('YouTube reply error: '.$e->getMessage()); return ['status' => false, 'error' => $e->getMessage()]; } } public function deleteComment($commentId) { try { $youtube = new Google_Service_YouTube($this->client); $youtube->comments->delete($commentId); return ['status' => true]; } catch (\Exception $e) { Log::error('YouTube delete comment error: '.$e->getMessage()); return ['status' => false, 'error' => $e->getMessage()]; } } public function refreshToken($refreshToken) { try { $this->client->refreshToken($refreshToken); return $this->client->getAccessToken(); } catch (\Exception $e) { Log::error('YouTube refresh token error: '.$e->getMessage()); return ['error' => $e->getMessage()]; } } public function refreshAccessToken($refreshToken, $expiresAt) { if (Carbon::now() < Carbon::parse($expiresAt)->subMinutes(10)) { return ['status' => false, 'error' => 'Access token is not expired']; } try { $newToken = $this->client->fetchAccessTokenWithRefreshToken($refreshToken); return [ 'status' => true, 'data' => [ 'access_token' => $newToken['access_token'], 'expires_at' => Carbon::now()->addSeconds($newToken['expires_in']) ] ]; } catch (\Exception $e) { return ['status' => false, 'error' => 'Failed to refresh access token: '.$e->getMessage()]; } } /* * 返回oss文件大小 (字节) * $pathName : 'path/to/your/file.txt' */ public function getOssFileSize($pathName) { // 配置信息 $accessKeyId = env('OSS_ACCESS_KEY_ID'); $accessKeySecret = env('OSS_ACCESS_KEY_SECRET'); $endpoint = env('OSS_ENDPOINT'); // 替换为你的 Region Endpoint $bucketName = env('OSS_BUCKET'); $objectName = $pathName; // OSS 文件路径 try { // 创建 OssClient 实例 $ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint); // 获取文件元数据 $metadata = $ossClient->headObject($bucketName, $objectName); // 从元数据中获取文件大小(字节) $fileSize = $metadata['content-length']; return ['status' => true, 'data' => $fileSize]; } catch (OssException $e) { // 错误处理 return ['status' => false, 'data' => $e->getMessage()]; } } }