|
@@ -130,119 +130,67 @@ class TwitterService implements SmmPlatformInterface
|
|
|
|
|
|
|
|
|
public function postVideo($message, $videoPath, $accessToken = null)
|
|
|
-
|
|
|
{
|
|
|
-
|
|
|
try {
|
|
|
-
|
|
|
- $this->twitterOAuth->setTimeouts(10, 30);
|
|
|
-
|
|
|
- //转为本地路径
|
|
|
-
|
|
|
+ $this->twitterOAuth->setTimeouts(10, 120);
|
|
|
+ $this->twitterOAuth->setApiVersion('1.1');
|
|
|
$videoPath = toStoragePath($videoPath);
|
|
|
-
|
|
|
- if (!file_exists($videoPath)) {
|
|
|
-
|
|
|
- throw new \Exception("视频文件不存在: {$videoPath}");
|
|
|
-
|
|
|
+ // Verify file exists and is readable
|
|
|
+ if (!file_exists($videoPath) || !is_readable($videoPath)) {
|
|
|
+ throw new Exception("Video file does not exist or is not readable: $videoPath");
|
|
|
}
|
|
|
|
|
|
- // 获取视频 MIME 类型
|
|
|
- $mimeType = mime_content_type($videoPath);
|
|
|
- if (strpos($mimeType, 'video/') !== 0) {
|
|
|
|
|
|
- throw new \Exception("文件不是有效的视频文件。MIME 类型: " . $mimeType);
|
|
|
+ // Upload video with chunked upload
|
|
|
+ $uploadedMedia = $this->twitterOAuth->upload('media/upload', [
|
|
|
+ 'media' => $videoPath,
|
|
|
+ 'media_category' => 'tweet_video'
|
|
|
+ ],['chunkedUpload'=>true]);
|
|
|
|
|
|
- }
|
|
|
-
|
|
|
- // 获取视频文件大小 (以字节为单位)
|
|
|
- $fileSize = filesize($videoPath);
|
|
|
+ $code = $this->twitterOAuth->getLastHttpCode();
|
|
|
|
|
|
- // 1.1版本上传媒体文件 - 初始化上传
|
|
|
- $initParams = [
|
|
|
- 'command' => 'INIT',
|
|
|
- 'media_type' => $mimeType,
|
|
|
- 'media_category' => 'tweet_video',
|
|
|
- 'total_bytes' => $fileSize,
|
|
|
- ];
|
|
|
- $uploadedInit = $this->twitterOAuth->upload('media/upload', $initParams, true);
|
|
|
+ if (isset($uploadedMedia->error) || $code != 200) {
|
|
|
+ throw new \Exception('媒体上传失败: ' . json_encode($uploadedMedia));
|
|
|
+ }
|
|
|
|
|
|
- if (!isset($uploadedInit->media_id_string)) {
|
|
|
+ $mediaId = $uploadedMedia->media_id_string;
|
|
|
|
|
|
- throw new \Exception('视频上传初始化失败: ' . json_encode($uploadedInit));
|
|
|
|
|
|
- }
|
|
|
- $mediaId = $uploadedInit->media_id_string;
|
|
|
-
|
|
|
- // 分段上传视频
|
|
|
- $fileHandle = fopen($videoPath, 'r');
|
|
|
- $segmentId = 0;
|
|
|
- while (!feof($fileHandle)) {
|
|
|
- $chunk = fread($fileHandle, 1024 * 1024); // 每次上传 1MB
|
|
|
- $segmentParams = [
|
|
|
- 'command' => 'APPEND',
|
|
|
- 'media_id' => $mediaId,
|
|
|
- 'segment_index' => $segmentId,
|
|
|
- 'media' => $chunk,
|
|
|
- ];
|
|
|
- $uploadedSegment = $this->twitterOAuth->upload('media/upload', $segmentParams, true);
|
|
|
- if (!empty($uploadedSegment->error)) {
|
|
|
- fclose($fileHandle);
|
|
|
- throw new \Exception('视频分段上传失败: ' . json_encode($uploadedSegment));
|
|
|
- }
|
|
|
- $segmentId++;
|
|
|
- }
|
|
|
- fclose($fileHandle);
|
|
|
-
|
|
|
- // 1.1版本上传媒体文件 - 完成上传
|
|
|
- $finalizeParams = [
|
|
|
- 'command' => 'FINALIZE',
|
|
|
- 'media_id' => $mediaId,
|
|
|
- ];
|
|
|
- $uploadedFinalize = $this->twitterOAuth->upload('media/upload', $finalizeParams, true);
|
|
|
- if (isset($uploadedFinalize->processing_info)) {
|
|
|
- $processingInfo = $uploadedFinalize->processing_info;
|
|
|
- $checkStatusSecs = $processingInfo->check_after_secs;
|
|
|
- $status = $processingInfo->state;
|
|
|
-
|
|
|
- while ($status !== 'succeeded' && $status !== 'failed') {
|
|
|
- sleep($checkStatusSecs);
|
|
|
- $statusParams = [
|
|
|
+ // 检查媒体处理状态
|
|
|
+ $processingInfo = $uploadedMedia->processing_info ?? null;
|
|
|
+ if ($processingInfo && $processingInfo->state === 'pending') {
|
|
|
+ $checkAfter = $processingInfo->check_after_secs;
|
|
|
+ sleep($checkAfter);
|
|
|
+ do {
|
|
|
+ $status = $this->twitterOAuth->get("media/upload", [
|
|
|
'command' => 'STATUS',
|
|
|
- 'media_id' => $mediaId,
|
|
|
- ];
|
|
|
- $uploadedStatus = $this->twitterOAuth->upload('media/upload', $statusParams, true);
|
|
|
- if (!empty($uploadedStatus->error)) {
|
|
|
- throw new \Exception('获取视频上传状态失败: ' . json_encode($uploadedStatus));
|
|
|
+ 'media_id' => $mediaId
|
|
|
+ ]);
|
|
|
+ dd($status);
|
|
|
+ if (isset($status->error)) {
|
|
|
+ throw new \Exception("媒体状态检查失败: " . json_encode($status));
|
|
|
}
|
|
|
- $processingInfo = $uploadedStatus->processing_info;
|
|
|
- $checkStatusSecs = $processingInfo->check_after_secs;
|
|
|
- $status = $processingInfo->state;
|
|
|
- if ($status === 'failed') {
|
|
|
- throw new \Exception('视频上传失败,Twitter 处理失败: ' . json_encode($uploadedStatus));
|
|
|
+ $processingInfo = $status->processing_info ?? null;
|
|
|
+ if (!$processingInfo || $processingInfo->state === 'failed') {
|
|
|
+ throw new \Exception("媒体处理失败: " . json_encode($status));
|
|
|
}
|
|
|
- }
|
|
|
- } elseif (isset($uploadedFinalize->error)) {
|
|
|
- throw new \Exception('视频上传完成步骤失败: ' . json_encode($uploadedFinalize));
|
|
|
+ if ($processingInfo->state !== 'succeeded') {
|
|
|
+ sleep($processingInfo->check_after_secs ?? 5);
|
|
|
+ }
|
|
|
+ } while ($processingInfo->state !== 'succeeded');
|
|
|
}
|
|
|
|
|
|
|
|
|
+
|
|
|
// 2.0版本发布推文
|
|
|
$this->twitterOAuth->setApiVersion('2');
|
|
|
$tweet = $this->twitterOAuth->post('tweets', [
|
|
|
'text' => $message,
|
|
|
- 'media' => ['media_ids' => [$mediaId]],
|
|
|
+ 'media' => ['media_ids' => [$mediaId]]
|
|
|
]);
|
|
|
|
|
|
- if (isset($tweet->errors)) {
|
|
|
- throw new \Exception('推文发布失败: ' . json_encode($tweet->errors));
|
|
|
- }
|
|
|
+ dd($tweet);
|
|
|
|
|
|
- return ['status' => true,
|
|
|
- 'data' => [
|
|
|
- 'responseIds' => [$tweet->data->id],
|
|
|
- ]
|
|
|
- ];
|
|
|
} catch (\Exception $e) {
|
|
|
return ['status' => false, 'data' => $e->getMessage()];
|
|
|
}
|