Google Cloud - Save Email Attachments (invoices) then Delete - php

I've coded the script below. This code connects to a Gmail account, searches for emails with attachments, and saves the attachments to Google Drive. It also deletes the emails from the Gmail account.
But anytime I run it:
Fatal error: Uncaught Google\Service\Exception: { "error": { "errors": [ { "domain": "global", "reason": "required", "message": "Login Required", "locationType": "header", "location": "Authorization" } ], "code": 401, "message": "Login Required" } }
I tried several changes. Nothing works. Even ChatGPT cannot find the fix/error.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Include the autoload file
require_once __DIR__ . '/vendor/autoload.php';
// Set your OAuth 2.0 Client IDs
$client_id = '123456789012-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com';
$client_secret = 'ABC-XYZ_123';
// Set your redirect URI
$redirect_uri = 'https://example.com/invoices';
// Create a new Google_Client object
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
// Set the scopes for the API services you want to use
$client->addScope(Google_Service_Drive::DRIVE);
// Authorize the client
$client->authorize();
// Create a new Google_Service_Drive object
$driveService = new Google_Service_Drive($client);
// Connect to Gmail
$hostname = '{imap.gmail.com:993/imap/ssl/novalidate-cert}INBOX';
$username = 'email#gmail.com';
$password = 'password';
// Connect to Gmail
$inbox = imap_open($hostname,$username,$password) or die('Cannot connect to Gmail: ' . imap_last_error());
// Get all emails from inbox
$emails = imap_search($inbox,'ALL');
// If emails are returned
if($emails) {
// Sort emails by date
rsort($emails);
// Loop through emails
foreach($emails as $email_number) {
// Get the full header of the email
$header = imap_fetchheader($inbox, $email_number);
// Parse the header into an object
$header_obj = imap_rfc822_parse_headers($header);
// Get the original sender's name
$sender_name = $header_obj->from[0]->personal;
// Get the date the email was sent
$date_sent = strtotime($header_obj->date);
$formatted_date_sent = date("Y-m-d", $date_sent);
// Get the attachments
$attachments = imap_fetchstructure($inbox,$email_number);
// If attachments are found
if(isset($attachments->parts) && count($attachments->parts)) {
// Loop through attachments
for($i = 0; $i < count($attachments->parts); $i++) {
// Check if the attachment is an object
if (is_object($attachments->parts[$i])) {
// Get the attachment data
$attachment_data = imap_fetchbody($inbox,$email_number,$i+1);
// Decode the attachment data
$attachment_data = base64_decode($attachment_data);
// Create the filename
$filename = $formatted_date_sent . ' ' . $sender_name;
// Save the attachment to Google Drive
$file = new Google_Service_Drive_DriveFile();
$file->setName($filename);
$file->setDescription('Invoice attachment');
$file->setMimeType('application/pdf');
$file->setParents(array('1nkfPyEvFYR_DGUGJDiy8WLx_ANUNsAdW'));
$createdFile = $driveService->files->create($file, array(
'data' => $attachment_data,
'mimeType' => 'application/pdf',
'uploadType' => 'multipart'
));
}
}
}
// Delete the email
imap_delete($inbox,$email_number);
}
}
// Close the connection
imap_close($inbox);
?>

From what I can see you are simply creating a drive service object but not acutally calling any authorization on it.
// Create a new Google_Service_Drive object
$driveService = new Google_Service_Drive($client);
You need to request consent of the user who's drive account you want to write to. With out that your service object is not authorized and you will get a "Login Required" error messages becouse your not logged in.
function getOauth2Client() {
try {
$client = buildClient();
// Set the refresh token on the client.
if (isset($_SESSION['refresh_token']) && $_SESSION['refresh_token']) {
$client->refreshToken($_SESSION['refresh_token']);
}
// If the user has already authorized this app then get an access token
// else redirect to ask the user to authorize access to Google Analytics.
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
// Set the access token on the client.
$client->setAccessToken($_SESSION['access_token']);
// Refresh the access token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$client->setAccessToken($client->getAccessToken());
$_SESSION['access_token'] = $client->getAccessToken();
}
return $client;
} else {
// We do not have access request access.
header('Location: ' . filter_var( $client->getRedirectUri(), FILTER_SANITIZE_URL));
}
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
}
Code ripped from Oauth2Authentication.php

Related

Let users upload videos from my site server to their youtube channel

I am running a WordPress site with ffmpeg where I allow users to create videos using a form. The videos work fine and are saved in their own directories per post.
I am now trying to allow users to upload the videos they created, to their own youtube channel.
Using PHP code from youtube api, and my app from google console, I am able to successfully upload a video to my own account. And it only asks for me to authenticate once.
PHP Code --
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/gap/google-api-php-client/vendor/autoload.php';
$key = file_get_contents('the_key.txt');
set_include_path($_SERVER['DOCUMENT_ROOT'] . '/gap/google-api-php-client/');
require_once 'src/Google/Client.php';
require_once 'src/Google/Service/YouTube.php';
$application_name = 'youtube4true';
$OAUTH2_CLIENT_ID = 'REMOVED FOR STACK';
$OAUTH2_CLIENT_SECRET = 'REMOVED FOR STACK';
$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$client->setScopes('https://www.googleapis.com/auth/youtube');
$redirect = filter_var('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);
$client->setAccessType("offline");
$client->setApprovalPrompt("force");
// Define an object that will be used to make all API requests.
$youtube = new Google_Service_YouTube($client);
// Check if an auth token exists for the required scopes
$tokenSessionKey = 'token-' . $client->prepareScopes();
if (isset($_GET['code'])) {
if (strval($_SESSION['state']) !== strval($_GET['state'])) {
die('The session state did not match.');
}
$client->authenticate($_GET['code']);
$_SESSION[$tokenSessionKey] = $client->getAccessToken();
header('Location: ' . $redirect);
}
if (isset($_SESSION[$tokenSessionKey])) {
$client->setAccessToken($_SESSION[$tokenSessionKey]);
$refresh_token = $_SESSION[$tokenSessionKey]['refresh_token'];
file_put_contents('the_key.txt', $refresh_token);
} elseif(file_exists('the_key.txt')) {
$refresh_token = file_get_contents('the_key.txt');
$client->refreshToken($refresh_token);
$_SESSION['token'] = $client->getAccessToken();
$access_token = $_SESSION['token']['access_token'];
$client->setAccessToken($access_token);
$refresh_token = $_SESSION['token']['refresh_token'];
file_put_contents('the_key.txt', $refresh_token);
}
// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
$htmlBody = '';
try{
// REPLACE this value with the path to the file you are uploading.
$videoPath = "HERE I WILL USE AJAX TO PASS THE PATH TO THE VIDEO FILE";
// Create a snippet with title, description, tags and category ID
// Create an asset resource and set its snippet metadata and type.
// This example sets the video's title, description, keyword tags, and
// video category.
$snippet = new Google_Service_YouTube_VideoSnippet();
$snippet->setTitle("Test title");
$snippet->setDescription("Test description");
$snippet->setTags(array("tag1", "tag2"));
// Numeric video category. See
// https://developers.google.com/youtube/v3/docs/videoCategories/list
$snippet->setCategoryId("22");
// Set the video's status to "public". Valid statuses are "public",
// "private" and "unlisted".
$status = new Google_Service_YouTube_VideoStatus();
$status->privacyStatus = "public";
// Associate the snippet and status objects with a new video resource.
$video = new Google_Service_YouTube_Video();
$video->setSnippet($snippet);
$video->setStatus($status);
// Specify the size of each chunk of data, in bytes. Set a higher value for
// reliable connection as fewer chunks lead to faster uploads. Set a lower
// value for better recovery on less reliable connections.
$chunkSizeBytes = 1 * 1024 * 1024;
// Setting the defer flag to true tells the client to return a request which can be called
// with ->execute(); instead of making the API call immediately.
$client->setDefer(true);
// Create a request for the API's videos.insert method to create and upload the video.
$insertRequest = $youtube->videos->insert("status,snippet", $video);
// Create a MediaFileUpload object for resumable uploads.
$media = new Google_Http_MediaFileUpload(
$client,
$insertRequest,
'video/*',
null,
true,
$chunkSizeBytes
);
$media->setFileSize(filesize($videoPath));
// Read the media file and upload it chunk by chunk.
$status = false;
$handle = fopen($videoPath, "rb");
while (!$status && !feof($handle)) {
$chunk = fread($handle, $chunkSizeBytes);
$status = $media->nextChunk($chunk);
}
fclose($handle);
// If you want to make other calls after the file upload, set setDefer back to false
$client->setDefer(false);
// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
try {
// Call the channels.list method to retrieve information about the
// currently authenticated user's channel.
$channelsResponse = $youtube->channels->listChannels('contentDetails', array(
'mine' => 'true',
));
$htmlBody = '';
foreach ($channelsResponse['items'] as $channel) {
// Extract the unique playlist ID that identifies the list of videos
// uploaded to the channel, and then call the playlistItems.list method
// to retrieve that list.
$uploadsListId = $channel['contentDetails']['relatedPlaylists']['uploads'];
$playlistItemsResponse = $youtube->playlistItems->listPlaylistItems('snippet', array(
'playlistId' => $uploadsListId,
'maxResults' => 50
));
$htmlBody .= "<h3>Videos in list $uploadsListId</h3><ul>";
foreach ($playlistItemsResponse['items'] as $playlistItem) {
$htmlBody .= sprintf('<li>%s (%s)</li>', $playlistItem['snippet']['title'],
$playlistItem['snippet']['resourceId']['videoId']);
}
$htmlBody .= '</ul>';
}
} catch (Google_ServiceException $e) {
$htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
} catch (Google_Exception $e) {
$htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
}
$_SESSION['token'] = $client->getAccessToken();
} else {
$state = mt_rand();
$client->setState($state);
$_SESSION['state'] = $state;
$authUrl = $client->createAuthUrl();
$htmlBody = <<<END
<h3>Authorization Required</h3>
<p>You need to authorise access before proceeding.<p>
END;
}
?>
<!doctype html>
<html>
<head>
<title>Video Uploaded</title>
</head>
<body>
<?=$htmlBody?>
</body>
</html>
I've also successfully been able to follow to javascript example to upload from my computer, but the files are stored on the server so that's not what I want.
With javascript, getting a new token for users seems to work, except the file upload issue and that users have to go into their Google accounts to revoke access/log out from my site app.
JS Code -
/*
Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.
*/
var signinCallback = function (result){
if(result.access_token) {
var uploadVideo = new UploadVideo();
uploadVideo.ready(result.access_token);
}
};
var STATUS_POLLING_INTERVAL_MILLIS = 60 * 1000; // One minute.
/**
* YouTube video uploader class
*
* #constructor
*/
var UploadVideo = function() {
/**
* The array of tags for the new YouTube video.
*
* #attribute tags
* #type Array.<string>
* #default ['google-cors-upload']
*/
this.tags = ['test'];
/**
* The numeric YouTube
* [category id](https://developers.google.com/apis-explorer/#p/youtube/v3/youtube.videoCategories.list?part=snippet&regionCode=us).
*
* #attribute categoryId
* #type number
* #default 22
*/
this.categoryId = 22;
/**
* The id of the new video.
*
* #attribute videoId
* #type string
* #default ''
*/
this.videoId = '';
this.uploadStartTime = 0;
};
UploadVideo.prototype.ready = function(accessToken) {
this.accessToken = accessToken;
this.gapi = gapi;
this.authenticated = true;
this.gapi.client.request({
path: '/youtube/v3/channels',
params: {
part: 'snippet',
mine: true
},
callback: function(response) {
if (response.error) {
console.log(response.error.message);
} else {
$('#channel-name').text(response.items[0].snippet.title);
$('#channel-thumbnail').attr('src', response.items[0].snippet.thumbnails.default.url);
$('.pre-sign-in').hide();
$('.post-sign-in').show();
}
}.bind(this)
});
$('#button').on("click", this.handleUploadClicked.bind(this));
};
/**
* Uploads a video file to YouTube.
*
* #method uploadFile
* #param {object} file File object corresponding to the video to upload.
*/
UploadVideo.prototype.uploadFile = function(file) {
var metadata = {
snippet: {
title: $('#title').val(),
description: $('#description').text(),
tags: this.tags,
categoryId: this.categoryId
},
status: {
privacyStatus: $('#privacy-status option:selected').text()
}
};
var uploader = new MediaUploader({
baseUrl: 'https://www.googleapis.com/upload/youtube/v3/videos',
file: file,
token: this.accessToken,
metadata: metadata,
params: {
part: Object.keys(metadata).join(',')
},
onError: function(data) {
var message = data;
// Assuming the error is raised by the YouTube API, data will be
// a JSON string with error.message set. That may not be the
// only time onError will be raised, though.
try {
var errorResponse = JSON.parse(data);
message = errorResponse.error.message;
} finally {
alert(message);
}
}.bind(this),
onProgress: function(data) {
var currentTime = Date.now();
var bytesUploaded = data.loaded;
var totalBytes = data.total;
// The times are in millis, so we need to divide by 1000 to get seconds.
var bytesPerSecond = bytesUploaded / ((currentTime - this.uploadStartTime) / 1000);
var estimatedSecondsRemaining = (totalBytes - bytesUploaded) / bytesPerSecond;
var percentageComplete = (bytesUploaded * 100) / totalBytes;
$('#upload-progress').attr({
value: bytesUploaded,
max: totalBytes
});
$('#percent-transferred').text(percentageComplete);
$('#bytes-transferred').text(bytesUploaded);
$('#total-bytes').text(totalBytes);
$('.during-upload').show();
}.bind(this),
onComplete: function(data) {
var uploadResponse = JSON.parse(data);
this.videoId = uploadResponse.id;
$('#video-id').text(this.videoId);
$('.post-upload').show();
this.pollForVideoStatus();
}.bind(this)
});
// This won't correspond to the *exact* start of the upload, but it should be close enough.
this.uploadStartTime = Date.now();
uploader.upload();
};
UploadVideo.prototype.handleUploadClicked = function() {
$('#button').attr('disabled', true);
this.uploadFile($('#file').get(0).files[0]);
};
UploadVideo.prototype.pollForVideoStatus = function() {
this.gapi.client.request({
path: '/youtube/v3/videos',
params: {
part: 'status,player',
id: this.videoId
},
callback: function(response) {
if (response.error) {
// The status polling failed.
console.log(response.error.message);
setTimeout(this.pollForVideoStatus.bind(this), STATUS_POLLING_INTERVAL_MILLIS);
} else {
var uploadStatus = response.items[0].status.uploadStatus;
switch (uploadStatus) {
// This is a non-final status, so we need to poll again.
case 'uploaded':
$('#post-upload-status').append('<li>Upload status: ' + uploadStatus + '</li>');
setTimeout(this.pollForVideoStatus.bind(this), STATUS_POLLING_INTERVAL_MILLIS);
break;
// The video was successfully transcoded and is available.
case 'processed':
$('#player').append(response.items[0].player.embedHtml);
$('#post-upload-status').append('<li>Final status.</li>');
break;
// All other statuses indicate a permanent transcoding failure.
default:
$('#post-upload-status').append('<li>Transcoding failed.</li>');
break;
}
}
}.bind(this)
});
};
I'm pretty sure it has to do with the refresh or access token. So how can I create an access token for each user instead of only allowing my account on the app?
You need to perform the OAUTH dance
Google has some good documentation on the process. The following quoted content and code is pulled from that site.
Your application identifies the permissions it needs.
Your application redirects the user to Google along with the list of requested permissions.
The user decides whether to grant the permissions to your application.
Your application finds out what the user decided.
If the user granted the requested permissions, your application retrieves tokens needed to make API requests on the user's behalf.
Code for steps 1 and 2:
// setup client (step.1)
$client = new Google_Client();
$client->setAuthConfig('client_secret.json');
$client->addScope(GOOGLE_SERVICE_YOUTUBE::YOUTUBE_UPLOAD);
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');
$client->setAccessType('offline'); // offline access
$client->setIncludeGrantedScopes(true); // incremental auth
// get url and redirect (step.2)
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
exit();
You'll need to setup your redirect url to accept the return call from them in the format something like:
[your_callback_url]/oauth2callback?state=state_parameter_passthrough_value&code=[code]&scope=[scope]
Then you extract the details from the callback and swap them for a token which will allow you to upload.
// swap the code for a token (step.5)
// … do client setup first
// then auth with the code
$client->authenticate($_GET['code']);
// retrieve the token
$access_token = $client->getAccessToken();
You can then use that token in your api calls to make calls on behalf of the user.
There is also a complete example on that same page.
I was able to get it working the way I wanted by using the following form on my page -
<form id="publish-youtube" action="/path/to/upload.php" method="post" target="_blank">
<input type="hidden" id="a_id" name="a_id" value="<?php echo $post_author_id; ?>" />
<input type="hidden" id="vid_id" name="vid_id" value="<?php echo $v_Id; ?>" />
<input type="submit" id="ytu_submit" name="ytu_submit" value="Publish to YouTube">
</form>
Then I followed the youtube api docs to create my app and my upload.php script, which asks the user to sign in if not already and it uploads the video based on the values from the form ( Note: Im using wordpress ) --
<?php
//require_once $_SERVER['DOCUMENT_ROOT'] . '/gap/google-api-php-client/vendor/autoload.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/wp-load.php';
set_include_path($_SERVER['DOCUMENT_ROOT'] . '/gap/google-api-php-client/');
require_once 'src/Google/Client.php';
require_once 'src/Google/Service/YouTube.php';
session_start();
$application_name = 'XXXX';
$OAUTH2_CLIENT_ID = 'XXXX';
$OAUTH2_CLIENT_SECRET = 'XXXX';
$videoTitle = $_POST['titlez'];
$authorID = $_POST['a_id'];
$vidID = $_POST['vid_id'];
$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$client->setScopes('https://www.googleapis.com/auth/youtube');
$redirect = filter_var('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);
$client->setAccessType("offline");
$client->setApprovalPrompt("force");
// Define an object that will be used to make all API requests.
$youtube = new Google_Service_YouTube($client);
// Check if an auth token exists for the required scopes
$tokenSessionKey = 'token-' . $client->prepareScopes();
if (isset($_GET['code'])) {
if (strval($_SESSION['state']) !== strval($_GET['state'])) {
die('The session state did not match.');
}
$client->authenticate($_GET['code']);
$_SESSION[$tokenSessionKey] = $client->getAccessToken();
header('Location: ' . $redirect);
}
if (isset($_SESSION[$tokenSessionKey])) {
$client->setAccessToken($_SESSION[$tokenSessionKey]);
}
get_header();
global $current_user, $imic_options; // Use global
get_currentuserinfo(); // Make sure global is set, if not set it.
if ((user_can($current_user, "administrator"))||(user_can($current_user, "edit_others_posts")) ):
// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
$htmlBody = '';
try{
// REPLACE this value with the path to the file you are uploading.
$videoPath = $_SERVER["DOCUMENT_ROOT"] ."/uploads/".$authorID."/".$vidID."/output-".$vidID.".mp4";
// Create a snippet with title, description, tags and category ID
// Create an asset resource and set its snippet metadata and type.
// This example sets the video's title, description, keyword tags, and
// video category.
$snippet = new Google_Service_YouTube_VideoSnippet();
$snippet->setTitle("Test title");
$snippet->setDescription("Test description");
$snippet->setTags(array("tag1", "tag2"));
// Numeric video category. See
// https://developers.google.com/youtube/v3/docs/videoCategories/list
$snippet->setCategoryId("22");
// Set the video's status to "public". Valid statuses are "public",
// "private" and "unlisted".
$status = new Google_Service_YouTube_VideoStatus();
$status->privacyStatus = "public";
// Associate the snippet and status objects with a new video resource.
$video = new Google_Service_YouTube_Video();
$video->setSnippet($snippet);
$video->setStatus($status);
// Specify the size of each chunk of data, in bytes. Set a higher value for
// reliable connection as fewer chunks lead to faster uploads. Set a lower
// value for better recovery on less reliable connections.
$chunkSizeBytes = 1 * 1024 * 1024;
// Setting the defer flag to true tells the client to return a request which can be called
// with ->execute(); instead of making the API call immediately.
$client->setDefer(true);
// Create a request for the API's videos.insert method to create and upload the video.
$insertRequest = $youtube->videos->insert("status,snippet", $video);
// Create a MediaFileUpload object for resumable uploads.
$media = new Google_Http_MediaFileUpload(
$client,
$insertRequest,
'video/*',
null,
true,
$chunkSizeBytes
);
$media->setFileSize(filesize($videoPath));
// Read the media file and upload it chunk by chunk.
$status = false;
$handle = fopen($videoPath, "rb");
while (!$status && !feof($handle)) {
$chunk = fread($handle, $chunkSizeBytes);
$status = $media->nextChunk($chunk);
}
fclose($handle);
// If you want to make other calls after the file upload, set setDefer back to false
$client->setDefer(false);
$htmlBody .= "<h3>Video Uploaded</h3><ul>";
$htmlBody .= sprintf('<li>%s</li>',
$status['snippet']['title']);
$htmlBody .= sprintf('<li>Video Link</li>',
$status['id']);
$htmlBody .= '</ul>';
} catch (Google_Service_Exception $e) {
$htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
} catch (Google_Exception $e) {
$htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
}
$_SESSION[$tokenSessionKey] = $client->getAccessToken();
} elseif ($OAUTH2_CLIENT_ID == '(I never changed this)REPLACE_ME') {
$htmlBody = <<<END
<h3>Client Credentials Required</h3>
<p>
You need to set <code>\$OAUTH2_CLIENT_ID</code> and
<code>\$OAUTH2_CLIENT_ID</code> before proceeding.
<p>
END;
} else {
// If the user hasn't authorized the app, initiate the OAuth flow
$state = mt_rand();
$client->setState($state);
$_SESSION['state'] = $state;
$authUrl = $client->createAuthUrl();
$htmlBody = <<<END
<h3>Authorization Required</h3>
<p>You need to authorize access before proceeding.<p>
END;
}
?>
<div id="ytu-container">
<?=$htmlBody?>
</div>
<?php
else: echo imic_unidentified_agent();
endif;
get_footer();
?>

Getting Authorization Required Error with youtube API call in CodeIgniter

I am new to youtube data API, I want to let users from my website to upload directly to my youtube channel but I am a beginner in this aspect and this is what my client ask me to do.
I tried to use the example on https://developers.google.com/youtube/v3/code_samples/php but all that I get is:
Authorization Required You need to authorize access before proceeding.
I don't know anything concerning this topic. I am using the CodeIgniter PHP framework. This is the sample code I copied from Google Developer:
<?php
public function youtube(){
if (!file_exists(__DIR__ . '/../party/to/google/vendor/autoload.php')) {
throw new \Exception('please run "composer require google/apiclient:~2.0" in "' . __DIR__ .'"');
}
require_once __DIR__ . '/../party/to/google/vendor/autoload.php';
//session_start();
/*
* You can acquire an OAuth 2.0 client ID and client secret from the
* {{ Google Cloud Console }} <{{ https://cloud.google.com/console }}>
* For more information about using OAuth 2.0 to access Google APIs, please see:
* <https://developers.google.com/youtube/v3/guides/authentication>
* Please ensure that you have enabled the YouTube Data API for your project.
*/
$OAUTH2_CLIENT_ID = 'My Cleint Id';
$OAUTH2_CLIENT_SECRET = 'My Client Secret';
$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$client->setScopes('https://www.googleapis.com/auth/youtube');
$redirect = filter_var('https:' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);
// Define an object that will be used to make all API requests.
$youtube = new Google_Service_YouTube($client);
// Check if an auth token exists for the required scopes
$tokenSessionKey = 'token-' . $client->prepareScopes();
if (isset($_GET['code'])) {
if (strval($_SESSION['state']) !== strval($_GET['state'])) {
$data['messages'] = 'The session state did not match.';
}
$client->authenticate($_GET['code']);
$_SESSION[$tokenSessionKey] = $client->getAccessToken();
redirect($redirect,'refresh');
}
if (isset($_SESSION[$tokenSessionKey])) {
$client->setAccessToken($_SESSION[$tokenSessionKey]);
}
// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
$data['messages'] = '';
try{
// REPLACE this value with the path to the file you are uploading.
$videoPath = base_url('path/to/thevideo/video.mp4');
// Create a snippet with title, description, tags and category ID
// Create an asset resource and set its snippet metadata and type.
// This example sets the video's title, description, keyword tags, and
// video category.
$snippet = new Google_Service_YouTube_VideoSnippet();
$snippet->setTitle("Test title");
$snippet->setDescription("Test description");
$snippet->setTags(array("tag1", "tag2"));
// Numeric video category. See
// https://developers.google.com/youtube/v3/docs/videoCategories/list
$snippet->setCategoryId("22");
// Set the video's status to "public". Valid statuses are "public",
// "private" and "unlisted".
$status = new Google_Service_YouTube_VideoStatus();
$status->privacyStatus = "public";
// Associate the snippet and status objects with a new video resource.
$video = new Google_Service_YouTube_Video();
$video->setSnippet($snippet);
$video->setStatus($status);
// Specify the size of each chunk of data, in bytes. Set a higher value for
// reliable connection as fewer chunks lead to faster uploads. Set a lower
// value for better recovery on less reliable connections.
$chunkSizeBytes = 1 * 1024 * 1024;
// Setting the defer flag to true tells the client to return a request which can be called
// with ->execute(); instead of making the API call immediately.
$client->setDefer(true);
// Create a request for the API's videos.insert method to create and upload the video.
$insertRequest = $youtube->videos->insert("status,snippet", $video);
// Create a MediaFileUpload object for resumable uploads.
$media = new Google_Http_MediaFileUpload(
$client,
$insertRequest,
'video/*',
null,
true,
$chunkSizeBytes
);
$media->setFileSize(filesize($videoPath));
// Read the media file and upload it chunk by chunk.
$status = false;
$handle = fopen($videoPath, "rb");
while (!$status && !feof($handle)) {
$chunk = fread($handle, $chunkSizeBytes);
$status = $media->nextChunk($chunk);
}
fclose($handle);
// If you want to make other calls after the file upload, set setDefer back to false
$client->setDefer(false);
$data['messages'] .= "<h3>Video Uploaded</h3><ul>";
$data['messages'] .= sprintf('<li>%s (%s)</li>',
$status['snippet']['title'],
$status['id']);
$data['messages'] .= '</ul>';
} catch (Google_Service_Exception $e) {
$data['messages'] .= sprintf('<p>A service error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
} catch (Google_Exception $e) {
$data['messages'] .= sprintf('<p>An client error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
}
$_SESSION[$tokenSessionKey] = $client->getAccessToken();
} elseif ($OAUTH2_CLIENT_ID == 'REPLACE_ME') {
$data['messages'] = <<<END
<h3>Client Credentials Required</h3>
<p>
You need to set <code>\$OAUTH2_CLIENT_ID</code> and
<code>\$OAUTH2_CLIENT_ID</code> before proceeding.
<p>
END;
} else {
// If the user hasn't authorized the app, initiate the OAuth flow
$state = mt_rand();
$client->setState($state);
$_SESSION['state'] = $state;
$authUrl = $client->createAuthUrl();
$data['messages'] = <<<END
<h3>Authorization Required</h3>
<p>You need to authorize access before proceeding.<p>
END;
}
$this->template->load('public_layout','contents','youtube_testing_view',$data);
}
?>

Google API fetching data error

When loading the token script :
token.php
<?php
require_once 'vendor/autoload.php';
session_start();
/*
* You can acquire an OAuth 2.0 client ID and client secret from the
* {{ Google Cloud Console }} <{{ https://cloud.google.com/console }}>
* For more information about using OAuth 2.0 to access Google APIs, please see:
* <https://developers.google.com/youtube/v3/guides/authentication>
* Please ensure that you have enabled the YouTube Data API for your project.
*/
$OAUTH2_CLIENT_ID = '516195178663-5pgnd88unc38khttmgj67scinlhk7caf.apps.googleusercontent.com'; // Enter your Client ID here
$OAUTH2_CLIENT_SECRET = 'QLsIeOBhHLIee4_d-OOqjblK'; // Enter your Client Secret here
$REDIRECT = filter_var('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'], FILTER_SANITIZE_URL);
$APPNAME = "BrickFame";
$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$client->setScopes('https://www.googleapis.com/auth/youtube');
$client->setRedirectUri($REDIRECT);
$client->setApplicationName($APPNAME);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
// Define an object that will be used to make all API requests.
$youtube = new Google_Service_YouTube($client);
if (isset($_GET['code'])) {
if (strval($_SESSION['state']) !== strval($_GET['state'])) {
die('The session state did not match.');
}
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
}
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
echo "Access Token: " . json_encode($_SESSION['token']);
}
// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
try {
// Call the channels.list method to retrieve information about the
// currently authenticated user's channel.
$channelsResponse = $youtube->channels->listChannels('contentDetails', array('mine' => 'true'));
$htmlBody = '';
foreach ($channelsResponse['items'] as $channel) {
// Extract the unique playlist ID that identifies the list of videos
// uploaded to the channel, and then call the playlistItems.list method
// to retrieve that list.
$uploadsListId = $channel['contentDetails']['relatedPlaylists']['uploads'];
$playlistItemsResponse = $youtube->playlistItems->listPlaylistItems('snippet', array(
'playlistId' => $uploadsListId,
'maxResults' => 50
));
$htmlBody .= "<h3>Videos in list $uploadsListId</h3><ul>";
foreach ($playlistItemsResponse['items'] as $playlistItem) {
$htmlBody .= sprintf('<li>%s (%s)</li>', $playlistItem['snippet']['title'],
$playlistItem['snippet']['resourceId']['videoId']);
}
$htmlBody .= '</ul>';
}
} catch (Google_ServiceException $e) {
$htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
} catch (Google_Exception $e) {
$htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
}
$_SESSION['token'] = $client->getAccessToken();
} else {
$state = mt_rand();
$client->setState($state);
$_SESSION['state'] = $state;
$authUrl = $client->createAuthUrl();
$htmlBody = <<<END
<h3>Authorization Required</h3>
<p>You need to authorise access before proceeding.<p>
END;
}
?>
<!doctype html>
<html>
<head>
<title>My Uploads</title>
</head>
<body>
<?php echo $htmlBody;?>
</body>
</html>
I can fetch my access token and refresh token is shown and i saved it under token.txt
Access Token: {"access_token":"ya29.Ci8tA-3KvN7LE03mLLWsJJOsKHPr3ArvEw9xDaCX8wpFxGM-VpLswYwR1Mnhc7wS1Q","token_type":"Bearer","expires_in":3600,"refresh_token":"1\/BopX8bClCDuDkg0WO9KKyvxKMC-y1GiFhFOO8JQ5HkU","created":1469621552}
However when accessing the upload script:
api.php
<?php
$key = file_get_contents('token.txt');
require_once 'vendor/autoload.php';
$client_id = '516195178663-5pgnd88unc38khttmgj67scinlhk7caf.apps.googleusercontent.com'; // Enter your Client ID here
$client_secret = 'QLsIeOBhHLIee4_d-OOqjblK'; // Enter your Client Secret here
$videoPath = "videos/example.mkv";
$videoTitle = "Just an Example Title";
$videoDescription = "This is the YouTube video's description";
$videoCategory = "22";
$videoTags = array("first tag","second tag","third tag");
try{
// Client init
$client = new Google_Client();
$client->setClientId($client_id);
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$client->setAccessToken($key);
$client->setClientSecret($client_secret);
if ($client->getAccessToken()) {
/**
* Check to see if our access token has expired. If so, get a new one and save it to file for future use.
*/
if($client->isAccessTokenExpired()) {
$newToken = json_decode($client->getAccessToken());
$client->refreshToken($newToken->refresh_token);
file_put_contents($key, $client->getAccessToken());
}
$youtube = new Google_Service_YouTube($client);
// Create a snipet with title, description, tags and category id
$snippet = new Google_Service_YouTube_VideoSnippet();
$snippet->setTitle($videoTitle);
$snippet->setDescription($videoDescription);
$snippet->setCategoryId($videoCategory);
$snippet->setTags($videoTags);
$snippet->setDefaultLanguage("en");
$snippet->setDefaultAudioLanguage("en");
$recordingDetails = new Google_Service_YouTube_VideoRecordingDetails();
$recordingDetails->setLocationDescription("United States of America");
$recordingDetails->setRecordingDate("2016-01-20T12:34:00.000Z");
$locationdetails = new Google_Service_YouTube_GeoPoint();
$locationdetails->setLatitude("38.8833");
$locationdetails->setLongitude("77.0167");
$recordingDetails->setLocation($locationdetails);
// Create a video status with privacy status. Options are "public", "private" and "unlisted".
$status = new Google_Service_YouTube_VideoStatus();
$status->setPrivacyStatus("public");
$status->setPublicStatsViewable(false);
$status->setEmbeddable(false); // Google defect still not editable https://code.google.com/p/gdata-issues/issues/detail?id=4861
// Create a YouTube video with snippet and status
$video = new Google_Service_YouTube_Video();
$video->setSnippet($snippet);
$video->setRecordingDetails($recordingDetails);
$video->setStatus($status);
// Size of each chunk of data in bytes. Setting it higher leads faster upload (less chunks,
// for reliable connections). Setting it lower leads better recovery (fine-grained chunks)
$chunkSizeBytes = 1 * 1024 * 1024;
// Setting the defer flag to true tells the client to return a request which can be called
// with ->execute(); instead of making the API call immediately.
$client->setDefer(true);
// Create a request for the API's videos.insert method to create and upload the video.
$insertRequest = $youtube->videos->insert("status,snippet,recordingDetails", $video);
// Create a MediaFileUpload object for resumable uploads.
$media = new Google_Http_MediaFileUpload(
$client,
$insertRequest,
'video/*',
null,
true,
$chunkSizeBytes
);
$media->setFileSize(filesize($videoPath));
// Read the media file and upload it chunk by chunk.
$status = false;
$handle = fopen($videoPath, "rb");
while (!$status && !feof($handle)) {
$chunk = fread($handle, $chunkSizeBytes);
$status = $media->nextChunk($chunk);
}
fclose($handle);
/**
* Video has successfully been uploaded, now lets perform some cleanup functions for this video
*/
if ($status->status['uploadStatus'] == 'uploaded') {
// Actions to perform for a successful upload
}
// If you want to make other calls after the file upload, set setDefer back to false
$client->setDefer(true);
} else{
// #TODO Log error
echo 'Problems creating the client';
}
} catch(Google_Service_Exception $e) {
print "Caught Google service Exception ".$e->getCode(). " message is ".$e->getMessage();
print "Stack trace is ".$e->getTraceAsString();
}catch (Exception $e) {
print "Caught Google service Exception ".$e->getCode(). " message is ".$e->getMessage();
print "Stack trace is ".$e->getTraceAsString();
}
?>
It is when i encountered the error : Here is the return value
Caught Google service Exception 0 message is refresh token must be passed in or set as part of setAccessTokenStack trace is #0 /home/vol11_2/freecluster.eu/fceu_18534127/htdocs/vendor/google/apiclient/src/Google/Client.php(246): Google_Client->fetchAccessTokenWithRefreshToken(NULL) #1 /home/vol11_2/freecluster.eu/fceu_18534127/htdocs/api.php(29): Google_Client->refreshToken(NULL) #2 {main}
What might i have missed? Could it be possibly my files error?
I dont understand the line that says : refresh token must be passed in or set as part of setAccessTokenStack trace is #0
Can anyone guide me out?
Thanks ! :)

read & send email with gmail api using service account

I need a cron to run in my shared host which will scan my gmail account every 30 minutes and will reply all unread email using PHP
First I downloaded the apiclient with composer -> { "require": { "google/apiclient": "1.*" } } . Then I create the service account(https://console.developers.google.com/apis/credentials/serviceaccountkey?project=xxxxxx) and get a json file which has these values :
"type": "service_account",
"project_id": "xxxxxx",
"private_key_id": "xyxyxyxyyxyxy",
"private_key": "-----BEGIN PRIVATE KEY-----\ndddddddddddddddmr\n-----END PRIVATE KEY-----\n",
"client_email": "yy-yyy#zzzz-zzzzzz.iam.gserviceaccount.com",
"client_id": "11763887676776474",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/xxxx%40zzz-zzzzzz.iam.gserviceaccount.com"
Also made sure gmail api is enabled(from).
Now I wondering How I connect with gmail and just read the un-read email using php...
I was modifying a example code but stuck here($apiKey =)...
<?php
include_once "vendor/google/templates/base.php";
echo pageHeader("Simple API Access");
require_once realpath(dirname(__FILE__) . '/vendor/google/apiclient/src/Google/autoload.php');
$client = new Google_Client();
$client->setApplicationName("Gmail access test");
$apiKey = "<YOUR_API_KEY>"; // WHICH VALUE i SHOULD PUT HERE????.
// Warn if the API key isn't changed.
if (strpos($apiKey, "<") !== false) {
echo missingApiKeyWarning();
exit;
}
$client->setDeveloperKey($apiKey);
$service = new Google_Service_Books($client);
...
...
...
UPDATE : my updated code looks like this
<?php
require_once realpath(dirname(__FILE__) . '/vendor/google/apiclient/src/Google/autoload.php');
session_start();
$scopes = array(
Google_Service_Gmail::GMAIL_READONLY,
Google_Service_Gmail::GMAIL_COMPOSE,
Google_Service_Gmail::GMAIL_SEND,
Google_Service_Gmail::MAIL_GOOGLE_COM,
);
$client = new Google_Client();
$arDetails = $client->loadServiceAccountJson('99b15f1f2326.json', $scopes);
$client->setAssertionCredentials($arDetails);
$client->setScopes($scopes);
$fl = $client->getAuth()->isAccessTokenExpired() ;
if($fl) {
$client->getAuth()->refreshTokenWithAssertion($arDetails);
}
$client->setAccessType('offline');
$gmailService = new Google_Service_Gmail($client);
#$users = $gmailService->users;
$users = $gmailService->users_messages;
$optParams['maxResults'] = 5; // Return Only 5 Messages
$optParams['labelIds'] = 'INBOX'; // Only show messages in Inbox
$messages = $gmailService->users_messages->listUsersMessages('me',$optParams);
echo '<br/><pre>';
print_r($users);
print_r($messages);
----
---
---
But I am getting error -
PHP Fatal error: Uncaught exception 'Google_Service_Exception' with message 'Error calling GET https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=5&labelIds=INBOX: (400) Bad Request' in /home3/shafico1/public_html/imsdev/frontend/web/atiq/vendor/google/apiclient/src/Google/Http/REST.php:110
Stack tra

Youtube Api - "Authorization Required You need to authorize access before proceeding" Error

I want video upload to Youtube with php. First, i enabled YouTube Data API v3 on Google Developer Page and i receive client id, client secret. And set my ip adress (Public API access)
I use this library https://github.com/google/google-api-php-client. And i tried this code
<?php
// Call set_include_path() as needed to point to your client library.
require_once 'src/Google/Client.php';
require_once 'src/Google/Service/YouTube.php';
session_start();
/*
* You can acquire an OAuth 2.0 client ID and client secret from the
* Google Developers Console <https://console.developers.google.com/>
* For more information about using OAuth 2.0 to access Google APIs, please see:
* <https://developers.google.com/youtube/v3/guides/authentication>
* Please ensure that you have enabled the YouTube Data API for your project.
*/
$OAUTH2_CLIENT_ID = 'xxx';
$OAUTH2_CLIENT_SECRET = 'xxx';
$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$client->setScopes('https://www.googleapis.com/auth/youtube');
$redirect = filter_var('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);
// Define an object that will be used to make all API requests.
$youtube = new Google_Service_YouTube($client);
if (isset($_GET['code'])) {
if (strval($_SESSION['state']) !== strval($_GET['state'])) {
die('The session state did not match.');
}
$client->authenticate($_GET['code']);
$_SESSION['token'] = $client->getAccessToken();
header('Location: ' . $redirect);
}
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
echo "asd";
try{
// REPLACE this value with the path to the file you are uploading.
$videoPath = "file.mp4";
// Create a snippet with title, description, tags and category ID
// Create an asset resource and set its snippet metadata and type.
// This example sets the video's title, description, keyword tags, and
// video category.
$snippet = new Google_Service_YouTube_VideoSnippet();
$snippet->setTitle("Test title");
$snippet->setDescription("Test description");
$snippet->setTags(array("tag1", "tag2"));
// Numeric video category. See
// https://developers.google.com/youtube/v3/docs/videoCategories/list
$snippet->setCategoryId("22");
// Set the video's status to "public". Valid statuses are "public",
// "private" and "unlisted".
$status = new Google_Service_YouTube_VideoStatus();
$status->privacyStatus = "public";
// Associate the snippet and status objects with a new video resource.
$video = new Google_Service_YouTube_Video();
$video->setSnippet($snippet);
$video->setStatus($status);
// Specify the size of each chunk of data, in bytes. Set a higher value for
// reliable connection as fewer chunks lead to faster uploads. Set a lower
// value for better recovery on less reliable connections.
$chunkSizeBytes = 1 * 1024 * 1024;
// Setting the defer flag to true tells the client to return a request which can be called
// with ->execute(); instead of making the API call immediately.
$client->setDefer(true);
// Create a request for the API's videos.insert method to create and upload the video.
$insertRequest = $youtube->videos->insert("status,snippet", $video);
// Create a MediaFileUpload object for resumable uploads.
$media = new Google_Http_MediaFileUpload(
$client,
$insertRequest,
'video/*',
null,
true,
$chunkSizeBytes
);
$media->setFileSize(filesize($videoPath));
// Read the media file and upload it chunk by chunk.
$status = false;
$handle = fopen($videoPath, "rb");
while (!$status && !feof($handle)) {
$chunk = fread($handle, $chunkSizeBytes);
$status = $media->nextChunk($chunk);
}
fclose($handle);
// If you want to make other calls after the file upload, set setDefer back to false
$client->setDefer(false);
$htmlBody .= "<h3>Video Uploaded</h3><ul>";
$htmlBody .= sprintf('<li>%s (%s)</li>',
$status['snippet']['title'],
$status['id']);
$htmlBody .= '</ul>';
} catch (Google_ServiceException $e) {
$htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
} catch (Google_Exception $e) {
$htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
htmlspecialchars($e->getMessage()));
}
$_SESSION['token'] = $client->getAccessToken();
} else {
// If the user hasn't authorized the app, initiate the OAuth flow
$state = mt_rand();
$client->setState($state);
$_SESSION['state'] = $state;
$authUrl = $client->createAuthUrl();
$htmlBody = <<<END
<h3>Authorization Required</h3>
<p>You need to authorize access before proceeding.<p>
END;
}
?>
<!doctype html>
<html>
<head>
<title>Video Uploaded</title>
</head>
<body>
<?=$htmlBody?>
</body>
</html>
I get error
"Authorization Required. You need to authorize access before proceeding."
message
What should i do? Thanks in advance
You don't need public API access for this.
If you create an OAuth2 access as an "installed application" and put clientid and secret back to this code in their places, that will work.

Categories