laravel - 有时无法将视频上传到 Twitter
问题描述
我有一个非常困难的情况。我已经花了 2 天时间,找不到解决方案。Laravel 上的项目。我想使用 Twitter API 端点将视频上传到 Twitter。但有时我会收到此错误:
文件当前不受支持
我按照官方文档视频规范和建议中的建议做了所有事情。在我的视频文件中设置音频编解码器为aac时出现错误,尽管在官方文档中是推荐的,但是当我将音频编解码器设置为mp3时,视频已上传,但音质非常好很差,有时根本没有声音。如果阅读起来很尴尬,请原谅我,但我想提供我所有的代码。因为我不知道如何解决这个问题了,我认为它可能会有所帮助。
<?php
namespace App\Jobs;
use App\Models\PublishedContent;
use Atymic\Twitter\Facades\Twitter;
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Bus\Queueable;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\File;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Str;
class PublishToTwitter implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* @var
*/
protected $publishingData;
/**
* Create a new job instance.
*
* @param $publishingData
*/
public function __construct($publishingData)
{
$this->publishingData = $publishingData;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$publishingData = $this->publishingData;
if (is_array($publishingData)) {
$publishingResult = $this->publishing(...array_values($publishingData));
sendNotification($publishingResult['message'], $publishingResult['status'], 'Twitter', $publishingResult['link'], $publishingData['post_name'], $publishingData['user']);
} else {
$scheduledData = processingScheduledPost($publishingData);
$postName = $scheduledData['scheduleData']['post_name'];
$postContent = $scheduledData['scheduleData']['post_content'];
$userToken = json_decode($publishingData->user_token,true);
$requestToken = [
'token' => $userToken['oauth_token'],
'secret' => $userToken['oauth_token_secret'],
];
$publishingResult = $this->publishing($scheduledData['file'], $postName, $postContent, $requestToken);
$publishingResult['status'] && PublishedContent::add($scheduledData['craft'], $scheduledData['file'], "twitter_share");
sendResultToUser($publishingData, $scheduledData['user'], $publishingResult['message'], $postName, $publishingResult['link'], $publishingResult['publishing_status'], $scheduledData['social_media']);
sendNotification($publishingResult['message'], $publishingResult['status'], 'Twitter', $publishingResult['link'], $postName, $scheduledData['user']);
}
}
/**
* @param $file
* @param $postName
* @param $postContent
* @param $requestToken
* @return array
*/
private function publishing($file, $postName, $postContent, $requestToken): array
{
$result = [
'status' => false,
'link' => null,
'message' => 'Your content can\'t successfully published on Twitter. This file is not supported for publishing.',
'publishing_status' => 'error'
];
if ((($file->refe_type !== 'text') || $file->refe_file_path) && !checkIfFileExist($file->refe_file_path)) {
$result['message'] = 'Missing or invalid file.';
return $result;
}
$filePath = $file->refe_file_path;
$fileSize = $file->content_length;
$tempFileName = 'temp-' . $file->refe_file_name;
$ext = $file->file_type;
$mediaCategory = 'tweet_' . $file->refe_type;
$mediaType = $file->refe_type . '/' . $ext;
$remoteFile = file_get_contents($filePath);
$tempFolder = public_path('/storage/uploads/temp');
if (!file_exists($tempFolder)) {
mkdir($tempFolder, 0777, true);
}
$tempFile = public_path('/storage/uploads/temp/' . $tempFileName);
File::put($tempFile, $remoteFile);
$convertedFileName = 'converted-' . $file->refe_file_name;
$convertedFile = public_path('/storage/uploads/temp/' . $convertedFileName);
$command = 'ffmpeg -y -i '.$tempFile.' -b:v 5000k -b:a 380k -c:a aac -profile:a aac_low -threads 1 '.$convertedFile.'';
exec($command);
@File::delete($tempFile);
try {
$twitter = Twitter::usingCredentials($requestToken['token'], $requestToken['secret']);
if ($file->refe_type === 'text') {
$twitter->postTweet([
'status' => urldecode($postContent),
'format' => 'json',
]);
$result['link'] = 'https://twitter.com/home';
$result['status'] = true;
$result['message'] = 'Your content successfully published on Twitter. You can visit to Twitter and check it.';
$result['publishing_status'] = 'done';
} else if ($file->refe_type === 'video' || $file->refe_type === 'image') {
if ($file->refe_type === 'video') {
$duration = getVideoDuration($file->refe_file_path);
if ($duration > config('constant.sharing_configs.max_video_duration.twitter')) {
throw new \Exception('The duration of the video file must not exceed 140 seconds.');
}
}
$isFileTypeSupported = checkPublishedFileType('twitter', $file->refe_type, strtolower($ext));
$isFileSizeSupported = checkPublishedFileSize('twitter', $file->refe_type, $fileSize, strtolower($ext));
if (!$isFileTypeSupported) {
throw new \Exception('Your content can\'t successfully published on Twitter. This file type is not supported for publishing.');
}
if (!$isFileSizeSupported) {
throw new \Exception('Your content can\'t successfully published on Twitter. The file size is exceeded.');
}
if ($file->refe_type === 'video') $fileSize = filesize($convertedFile);
if (strtolower($ext) === 'gif') {
$initMedia = $twitter->uploadMedia([
'command' => 'INIT',
'total_bytes' => (int)$fileSize
]);
} else {
$initMedia = $twitter->uploadMedia([
'command' => 'INIT',
'media_type' => $mediaType,
'media_category' => $mediaCategory,
'total_bytes' => (int)$fileSize
]);
}
$mediaId = (int)$initMedia->media_id_string;
$fp = fopen($convertedFile, 'r');
$segmentId = 0;
while (!feof($fp)) {
$chunk = fread($fp, 1048576);
$twitter->uploadMedia([
'media_data' => base64_encode($chunk),
'command' => 'APPEND',
'segment_index' => $segmentId,
'media_id' => $mediaId
]);
$segmentId++;
}
fclose($fp);
$twitter->uploadMedia([
'command' => 'FINALIZE',
'media_id' => $mediaId
]);
if ($file->refe_type === 'video') {
$waits = 0;
while ($waits <= 4) {
// Authorizing header for Twitter API
$oauth = [
'command' => 'STATUS',
'media_id' => $mediaId,
'oauth_consumer_key' => config('twitter.consumer_key'),
'oauth_nonce' => Str::random(42),
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_timestamp' => time(),
'oauth_token' => $requestToken['token'],
'oauth_version' => '1.0'
];
// Generate an OAuth 1.0a HMAC-SHA1 signature for an HTTP request
$baseInfo = $this->buildBaseString('https://upload.twitter.com/1.1/media/upload.json', 'GET', $oauth);
// Getting a signing key
$compositeKey = rawurlencode(config('twitter.consumer_secret')) . '&' . rawurlencode($requestToken['secret']);
// Calculating the signature
$oauthSignature = base64_encode(hash_hmac('sha1', $baseInfo, $compositeKey, true));
$oauth['oauth_signature'] = $oauthSignature;
$headers['Authorization'] = $this->buildAuthorizationHeader($oauth);
try {
$guzzle = new GuzzleClient([
'headers' => $headers
]);
$response = $guzzle->request( 'GET', 'https://upload.twitter.com/1.1/media/upload.json?command=STATUS&media_id=' . $mediaId);
$uploadStatus = json_decode($response->getBody()->getContents());
} catch (\Exception | GuzzleException $e) {
dd($e->getMessage(), $e->getLine(), $e->getFile());
}
if (isset($uploadStatus->processing_info->state)) {
switch ($uploadStatus->processing_info->state) {
case 'succeeded':
$waits = 5; // break out of the while loop
break;
case 'failed':
File::delete($tempFile);
Log::error('File processing failed: ' . $uploadStatus->processing_info->error->message);
throw new \Exception('File processing failed: ' . $uploadStatus->processing_info->error->message);
default:
sleep($uploadStatus->processing_info->check_after_secs);
$waits++;
}
} else {
throw new \Exception('There was an unknown error uploading your file');
}
}
}
$twitter->postTweet(['status' => urldecode($postContent), 'media_ids' => $initMedia->media_id_string]);
@File::delete($convertedFile);
$result['link'] = 'https://twitter.com/home';
$result['status'] = true;
$result['message'] = 'Your content successfully published on Twitter. You can visit to Twitter and check it.';
$result['publishing_status'] = 'done';
}
} catch (\Exception $e) {
dd($e->getMessage());
$result['message'] = $e->getMessage();
return $result;
}
return $result;
}
/**
* @param $baseURI
* @param $method
* @param $params
* @return string
*
* Creating the signature base string
*/
protected function buildBaseString($baseURI, $method, $params): string
{
$r = array();
ksort($params);
foreach($params as $key=>$value){
$r[] = "$key=" . rawurlencode($value);
}
return $method . "&" . rawurlencode($baseURI) . '&' . rawurlencode(implode('&', $r));
}
/**
* @param $oauth
* @return string
*
* Collecting parameters
*/
protected function buildAuthorizationHeader($oauth): string
{
$r = 'OAuth ';
$values = array();
foreach($oauth as $key=>$value)
$values[] = "$key=\"" . rawurlencode($value) . "\"";
$r .= implode(', ', $values);
return $r;
}
}
如果有人能帮助我,我将不胜感激。