Google Photos API + python: Working non-deprecated example - python

I've been finding such a mix of code examples. But nothing with a maintained library (google-auth) + full working example. google-api-python-client and oauth2client are no longer supported (https://github.com/googleapis/google-api-python-client/issues/651).
Here's a working example with deprecated libraries, but I'd like to see some examples that allow full access to the api (searching by albumId currently doesn't work with this library):
from apiclient.discovery import build
from httplib2 import Http
from oauth2client import file, client, tools
# Setup the Photo v1 API
SCOPES = 'https://www.googleapis.com/auth/photoslibrary.readonly'
store = file.Storage('credentials.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('photoslibrary', 'v1', http=creds.authorize(Http()))
# Call the Photo v1 API
results = service.albums().list(
pageSize=10, fields="nextPageToken,albums(id,title)").execute()
items = results.get('albums', [])
if not items:
print('No albums found.')
else:
print('Albums:')
for item in items:
print('{0} ({1})'.format(item['title'].encode('utf8'), item['id']))

You want to use google_auth instead of oauth2client, because oauth2client is deprecated.
You have already been able to use Photo API.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
For example, the sample script for authorizing can be seen at the Quickstart of Drive API with python. You can see the method for installing the library. Using this, your script can be modified as follows.
Modified script:
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
def main():
credentialsFile = 'credentials.json' # Please set the filename of credentials.json
pickleFile = 'token.pickle' # Please set the filename of pickle file.
SCOPES = ['https://www.googleapis.com/auth/photoslibrary']
creds = None
if os.path.exists(pickleFile):
with open(pickleFile, 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
credentialsFile, SCOPES)
creds = flow.run_local_server()
with open(pickleFile, 'wb') as token:
pickle.dump(creds, token)
service = build('photoslibrary', 'v1', credentials=creds)
# Call the Photo v1 API
results = service.albums().list(
pageSize=10, fields="nextPageToken,albums(id,title)").execute()
items = results.get('albums', [])
if not items:
print('No albums found.')
else:
print('Albums:')
for item in items:
print('{0} ({1})'.format(item['title'].encode('utf8'), item['id']))
if __name__ == '__main__':
main()
About the script for retrieving the album list, your script was used.
When you run this script, at first, the authorization process is run. So please authorize the scope. This process is required to run only one time. But if you want to change the scopes, please delete the pickle file and authorize again.
References:
the Quickstart of Drive API with python
google-auth-library-python
google_auth_oauthlib package
Method: albums.list
If I misunderstood your question and this was not the direction you want, I apologize.
Added 1:
If you want to use the method of mediaItems.search, how about the following sample script? About the script for authorizing, please use above script.
Sample script:
service = build('photoslibrary', 'v1', credentials=creds)
albumId = '###' # Please set the album ID.
results = service.mediaItems().search(body={'albumId': albumId}).execute()
print(results)
Added 2:
You want to remove googleapiclient from my proposed above sample script.
You want to retrieve the access token using google_auth_oauthlib.flow and google.auth.transport.requests.
You want to retrieve the media item list in the specific album using request of python without googleapiclient.
I think that in this case, "Method: mediaItems.search" is suitable.
If my understanding is correct, how about this sample script?
Sample script:
Before you use this script, please set the variable of albumId.
from __future__ import print_function
import json
import pickle
import os.path
import requests
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
def main():
credentialsFile = 'credentials.json'
pickleFile = 'token.pickle'
SCOPES = ['https://www.googleapis.com/auth/photoslibrary.readonly']
creds = None
if os.path.exists(pickleFile):
with open(pickleFile, 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
credentialsFile, SCOPES)
creds = flow.run_local_server()
with open(pickleFile, 'wb') as token:
pickle.dump(creds, token)
albumId = '###' # <--- Please set the album ID.
url = 'https://photoslibrary.googleapis.com/v1/mediaItems:search'
payload = {'albumId': albumId}
headers = {
'content-type': 'application/json',
'Authorization': 'Bearer ' + creds.token
}
res = requests.post(url, data=json.dumps(payload), headers=headers)
print(res.text)
if __name__ == '__main__':
main()
Note:
In this case, you can use the scope of both https://www.googleapis.com/auth/photoslibrary.readonly and https://www.googleapis.com/auth/photoslibrary.
Reference:
Method: mediaItems.search

Related

I can't authorize gmail api application in google colaboratory

I'm running the quickstart code from https://developers.google.com/people/quickstart/python in a colab notebook.
# \[START people_quickstart\]
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = \['https://www.googleapis.com/auth/contacts.readonly'\]
def main():
"""Shows basic usage of the People API.
Prints the name of the first 10 connections.
"""
creds = None
\# The file token.json stores the user's access and refresh tokens, and is
\# created automatically when the authorization flow completes for the first
\# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
\# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
\# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('people', 'v1', credentials=creds)
# Call the People API
print('List 10 connection names')
results = service.people().connections().list(
resourceName='people/me',
pageSize=10,
personFields='names,emailAddresses').execute()
connections = results.get('connections', [])
for person in connections:
names = person.get('names', [])
if names:
name = names[0].get('displayName')
print(name)
except HttpError as err:
print(err)
if __name__ == '__main__':
main()
# \[END people_quickstart\]
but it fails the authentication at this stage:
http://localhost:52591/?state=K8nzFjxOrWJkPEqjeG1AZiGpsT5DSx&code=4/0ARtbsJoAH2rD9UYgHOKJ__UdJcq87d2vuFjEAqcI3aKJpj1rLJ-93TXR0_v-LnBR4Fytsg&scope=https://www.googleapis.com/auth/gmail.readonly
why is it redirected to localhost?
There is a simple way to send e-mail at google colab? with or without using gmail?
i'm using the google colab at opera browser.
Can anyone help me how i can send a simple e-mail at google colab without lowing the gmail security level?
T.T
There is something wrong with the way you are loading the run_local_server if you are getting a 404 error.
The code below is my standard QuickStart for People api. I just tested it and it works fine. I am not getting a 404 error.
# To install the Google client library for Python, run the following command:
# pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
from __future__ import print_function
import os.path
import google.auth.exceptions
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/contacts']
def main():
"""Shows basic usage of the People API.
Prints a list of user contacts.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
try:
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
creds.refresh(Request())
except google.auth.exceptions.RefreshError as error:
# if refresh token fails, reset creds to none.
creds = None
print(f'An error occurred: {error}')
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'C:\YouTube\dev\credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('people', 'v1', credentials=creds)
# Call the People API
print('List 10 connection names')
results = service.people().connections().list(
resourceName='people/me',
pageSize=10,
personFields='names,emailAddresses').execute()
connections = results.get('connections', [])
for person in connections:
names = person.get('names', [])
if names:
name = names[0].get('displayName')
print(name)
except HttpError as err:
print(err)
if __name__ == '__main__':
main()

How to get Access Token for uploading files on google drive

headers = {"Authorization": "Bearer " + ACCESS_TOKEN}
folder=Folder_id // google drive folder Id
para = {"title": assignment_file_name,
"parents": [{"id": "root"}, {'id': folder}]}
files = {
"data": ("metadata", json.dumps(para), "application/json; charset=UTF-8"),
"file": assignment_file.stream.read()
}
response = requests.get("https://drive.google.com/drive/folders/"+Folder_id,
headers= headers, files=files)
I want to upload file fetched from requests to google drive folder.
But From where to get this ACCESS_TOKEN in the headers variable?
The access token is the result of the Oauth2 request. When you want to access private user data you must first ask for the users consent to your application accessing their data. To do this we use Oauth2. If you are interested in knowing how it works i have a video on it Understanding Oauth2 with curl..
Rather then coding all this manually you should consider using the Google Api python client library. As shown in the official Python quickstart for Drive api will walk you though how to create credentials (be sure to enable drive api) for your application and then how to request access of the user.
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
return
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
except HttpError as error:
# TODO(developer) - Handle errors from drive API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()

Is there any specific way to filter Google Drive changes?

I started working with the Google Drive Changes API (https://developers.google.com/drive/api/v3/reference/changes). While my code can track all changes happening in My Drive, I cannot seem to filter out only specific changes like creation of new files. Whenever I'm uploading a file to a folder, there are 2 changes occurring, one which says about the change in file, and the other which says about the change in the folder.
My code so far is as follows:
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
import schedule
import time
# If modifying these scopes, delete the file token.pickle.
SCOPES = [
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.metadata',
'https://www.googleapis.com/auth/drive.photos.readonly',
]
def job():
print("Inside job()")
global saved_start_page_token
page_token = saved_start_page_token
while page_token is not None:
response = service.changes().list(pageToken=page_token, spaces='drive', restrictToMyDrive=True).execute()
for change in response.get('changes'):
# Process change
file_id = change.get('fileId')
print('Change found for file: %s' % file_id)
print('Change type: %s' % change.get('changeType'))
print('Filename: %s' % change.get('file').get('name'))
if 'newStartPageToken' in response:
# Last page, save this token for the next polling interval
saved_start_page_token = response.get('newStartPageToken')
page_token = response.get('nextPageToken')
def main():
creds = None
pickle_file = 'token.pickle'
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists(pickle_file):
with open(pickle_file, 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open(pickle_file, 'wb') as token:
pickle.dump(creds, token)
global service
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
response = service.changes().getStartPageToken().execute()
global saved_start_page_token
saved_start_page_token = response.get('startPageToken')
schedule.every().minute.do(job)
while True:
schedule.run_pending()
time.sleep(1)
if __name__ == '__main__':
main()
Can I filter out changes to only show file/folder creation changes?

How to get directory path in Google Drive API

Currently, I am able to retrieve the Folders/Directory that owned by me and its returning a list of result as expected.
Here is the code snippet,
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.metadata',
'https://www.googleapis.com/auth/drive.photos.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
queries = [
"mimeType = 'application/vnd.google-apps.folder'",
"'my_email#gmail.com' in owners"
]
drive_str_query = queries[0] if len(queries) == 1 else " and ".join(queries)
results = service.files().list(q=drive_str_query,
pageSize=1000,
spaces='drive',
fields="nextPageToken, files(*)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
else:
print('Files:')
for item in items:
print(item)
if __name__ == '__main__':
main()
This snippet returns data with sufficient fields as mentioned in here, But, it doesn't include a relative/absolute path to the Folder/Directory
Is there any way to findout the path to the directory/folder?

Google App Script API cannot authenticate "Request contains an invalid argument"

I am pretty much using the sample from google's own site
https://developers.google.com/apps-script/api/how-tos/execute
The relevant part of the sample python script is replicated below
from __future__ import print_function
from googleapiclient import errors
from googleapiclient.discovery import build
from httplib2 import Http
from oauth2client import file as oauth_file, client, tools
def main():
"""Runs the sample.
"""
SCRIPT_ID = 'ENTER_YOUR_SCRIPT_ID_HERE'
# Setup the Apps Script API
SCOPES = 'https://www.googleapis.com/auth/script.projects'
store = oauth_file.Storage('token.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('credentials.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('script', 'v1', http=creds.authorize(Http()))
if __name__ == '__main__':
main()
I am getting the following error
File "test.py", line 67, in <module>
main()
File "test.py", line 22, in main
service = build('script', 'v1', http=creds.authorize(Http()))
File "C:\Users\pedxs\Anaconda2\lib\site-packages\googleapiclient\_helpers.py", line 130, in positional_wrapper
return wrapped(*args, **kwargs)
File "C:\Users\pedxs\Anaconda2\lib\site-packages\googleapiclient\discovery.py", line 232, in build
raise e
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://www.googleapis.com/discovery/v1/apis/script/v1/rest returned "Request contains an invalid argument.">
I had this exact code working just a week ago. line 22 uses the discovery build function, which as I understand is sending credentials to Google's API authenticator server "https://www.googleapis.com/discovery/v1/apis/script/v1/rest". I am suspecting this is a problem on Google's side because even their sample code does not work.
I have tried creating a new Google Cloud Platform and getting a new credentials.json file. I also tried authenticating with a different email account.
i encountered same error.
i have used that code more than 1 year ago.
but suddenly i could not use since Feb-23.
so i adopted another method, and i used post request with oauth2.
i think you need your google service account renew.
i think something change with scope ui...
good luck!
■python code
from oauth2client import client
def request_to_gas():
credentials = client.OAuth2Credentials(
access_token=None,
client_id={your_client_id},
client_secret={your_client_secret},
refresh_token={your_refresh_token},
token_expiry=None,
token_uri=GOOGLE_TOKEN_URI,
user_agent=None,
revoke_uri=GOOGLE_REVOKE_URI)
credentials.refresh(httplib2.Http()) # refresh the access token
my_url = "your_google_apps_script_web_url"
myheaders = {'Authorization': 'Bearer {}'.format(credentials.access_token),
"Content-Type": "application/json"
}
response = requests.post(my_url,
data=json.dumps({
'localdate' : '2019/02/23 12:12:12'}),
headers=myheaders
)
add thins code and publish as web app.
■google apps script code
function doPost(e) {
var params = JSON.parse(e.postData.getDataAsString()); // ※
var value = params.localdate; // get python code -- 2019/02/23 12:12:12
// here is your google apps script api
do_something();
var output = ContentService.createTextOutput();
output.setMimeType(ContentService.MimeType.JSON);
output.setContent(JSON.stringify({ message: "success!" }));
return output;
}
In your situation, there are 2 patterns.
Pattern 1:
Use authorization script at Quickstart.
Sample script:
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
def main():
# Setup the Apps Script API
SCOPES = ['https://www.googleapis.com/auth/script.projects', 'https://www.googleapis.com/auth/drive']
creds = None
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'client_secret.json', SCOPES)
creds = flow.run_local_server()
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('script', 'v1', credentials=creds)
scriptId = "### script ID ###" # Please set this
request = {"function": "myFunction", "parameters": ["sample"], "devMode": True}
response = service.scripts().run(body=request, scriptId=scriptId).execute()
print(response)
if __name__ == '__main__':
main()
Pattern 2:
If you want to use the script in your question, please modify your script as follows. This is discussed at here and here.
Sample script:
from __future__ import print_function
from googleapiclient.discovery import build
from oauth2client import file as oauth_file, client, tools
def main():
SCOPES = ['https://www.googleapis.com/auth/script.projects', 'https://www.googleapis.com/auth/drive']
store = oauth_file.Storage('token.json')
creds = store.get()
if not creds or creds.invalid:
flow = client.flow_from_clientsecrets('client_secret.json', SCOPES)
creds = tools.run_flow(flow, store)
service = build('script', 'v1', credentials=creds)
scriptId = "### script ID ###" # Please set this
request = {"function": "myFunction", "parameters": ["sample"], "devMode": True}
response = service.scripts().run(body=request, scriptId=scriptId).execute()
print(response)
if __name__ == '__main__':
main()
Sample script of GAS side:
function myFunction(e) {
return "ok: " + e;
}
Result:
You can retrieve the following response from above both scripts.
{
"response": {
"#type": "type.googleapis.com/google.apps.script.v1.ExecutionResponse",
"result": "ok: sample"
},
"done": True
}
Note:
Before you use above scripts, please setup for using it. You can see the official document at below.
Executing Functions using the Apps Script API
Method: scripts.run
In my environment, I could confirm that above both patterns can be used. But if in your environment, those were not used, I apologize.

Categories