log in to Gmail/YouTube programmatically (+ YouTube API)

Get help with using AutoHotkey and its commands and hotkeys
User avatar
jeeswg
Posts: 6125
Joined: 19 Dec 2016, 01:58
Location: UK

log in to Gmail/YouTube programmatically (+ YouTube API)

28 May 2017, 20:43

I'm planning to release various YouTube and YouTube API-related scripts and functions. I have everything sussed except actually logging in to YouTube.

Is there a way to log in to Gmail/YouTube directly without setting the text of and clicking web elements in Internet Explorer? Perhaps by using a url with username/password parameters or WinHttpRequest. Cheers.

To use the YouTube API in a passive way, e.g. to get a list of videos in a playlist, get a video's title/duration/date, get channel(/user) information, you don't need to log into Gmail/YouTube. But to make active changes to an account e.g. create a playlist, add videos to a playlist, you need to log in to Gmail/YouTube, and you need to click a button to confirm you want the API to be able to make changes to your account. So there are two logins required.

Btw some other sites of interest about which I might ask the same question: AutoHotkey, Stack Overflow, GitHub (although I don't use it yet), social media e.g. Twitter/Facebook/Instagram/Pinterest. Again, in IE, but not via set text/send click. Thanks.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
BoBo
Posts: 2518
Joined: 13 May 2014, 17:15

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

29 May 2017, 09:55

https://github.com/h2non/youtube-video-api
Straightforward node.js/io.js programmatic and command-line interface to easily upload, list, update, rate, thumbnail and delete videos from YouTube using OAuth2 and Google API v3.

It was designed to provide a reliable server-to-server automation solution, with additional support for transparent Google API OAuth2 token negotiation retrieval using Nightmare + Electron in case that you don't have a valid OAuth2 token or simply you want to use a fresh token every time automatically
Hope that helps. I guess Electron/node.js has been dicussed already at this forum.
User avatar
jeeswg
Posts: 6125
Joined: 19 Dec 2016, 01:58
Location: UK

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

05 Jun 2017, 21:03

Thanks for the link, good call. Despite looking through the code, and many Internet searches, I still haven't worked out how to login directly, without using Internet Explorer.

Here is the code I have so far:

Code: Select all

;you need to set these 6 fields, before the script can begin
;the script works, but I want to replace the 'get vAuthCode' section

vJeeYouTubeApiKey = #######################################
vJeeYouTubeClientID = #############################################.apps.googleusercontent.com
vJeeYouTubeClientSecret = ########################
vChannelID = ########################
vPlaylistID = ##################################
vVideoID = ###########
JEE_YouTubePlaylistAddVid(vChannelID, vPlaylistID, vVideoID)
Run, https://www.youtube.com/channel/%vChannelID%
return

;==================================================

JEE_YouTubePlaylistAddVid(vChannelID, vPlaylistID, vVideoID, vPos="")
{
	;[CHECK] vPos not currently handled
	global vJeeYouTubeApiKey ;e.g. 39 chars
	global vJeeYouTubeClientID ;e.g. 72 chars e.g. #############################################.apps.googleusercontent.com
	global vJeeYouTubeClientSecret ;e.g. 24 chars
	if (vJeeYouTubeApiKey vJeeYouTubeClientID vJeeYouTubeClientSecret = "")
	{
		MsgBox, % "error: login details missing"
		return
	}
	vRedirectUri = http://localhost
	vUrl1 = https://accounts.google.com/o/oauth2/auth?client_id=%vJeeYouTubeClientID%&redirect_uri=%vRedirectUri%&scope=https://gdata.youtube.com&response_type=code&access_type=offline

	;===============
	;START - get vAuthCode, replace this if possible (at this point I sign in to Google (email and password) and/or click the Allow button)
	oWB := ComObjCreate("InternetExplorer.Application")
	oWB.Visible := true
	;oWB.Visible := false
	oWB.Navigate(vUrl1)
	DetectHiddenWindows, On
	hWnd := oWB.HWND
	vIsV1 := !!SubStr(1,0)
	Loop
	{
		WinGetTitle, vWinTitle, % "ahk_id " hWnd
		ControlGetText, vText, Edit1, % "ahk_id " hWnd
		if (SubStr(vText, vIsV1-1) = "#")
			break
		Sleep 1000
	}
	vPos := InStr(vText, "=")
	vAuthCode := SubStr(vText, vPos+1)
	MsgBox, % vAuthCode
	;END - get vAuthCode
	;===============

	vUrl2 = https://accounts.google.com/o/oauth2/token
	oHTTP1 := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP1.Open("POST", vUrl2, false)
	oHTTP1.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
	oHTTP1.SetRequestHeader("Host", "accounts.google.com")

	vPostData1 = ;continuation section
	(Join& LTrim
	code=%vAuthCode%
	client_id=%vJeeYouTubeClientID%
	client_secret=%vJeeYouTubeClientSecret%
	redirect_uri=%vRedirectUri%
	grant_type=authorization_code
	)
	MsgBox, % vPostData1

	oHTTP1.Send(vPostData1)
	vText := oHTTP1.ResponseText
	MsgBox, % vText

	vAccessToken := ""
	Loop, Parse, vText, `n, `r
		if A_LoopField contains "access_token"
			vPos1 := InStr(A_LoopField, ":"), vPos2 := InStr(A_LoopField, """", 0, 0), vAccessToken := SubStr(A_LoopField, vPos1+3, vPos2-vPos1-3)

	vUrl3 = https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&key=%vJeeYouTubeApiKey%&access_token=%vAccessToken%
	vPostData2 = ;continuation section
	(LTrim
	{
	  "snippet": {
	    "playlistId": "%vPlaylistID%",
	    "resourceId": {
	      "kind": "youtube#video",
	      "videoId": "%vVideoID%"
	    }
	  }
	}
	)
	MsgBox, % vPostData2

	oHTTP2 := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP2.Open("POST", vUrl3 , False)
	oHTTP2.SetRequestHeader("Content-Type", "application/json")
	oHTTP2.Send(vPostData2)
	vText := oHTTP2.ResponseText
	MsgBox, % vText
	return
}
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6125
Joined: 19 Dec 2016, 01:58
Location: UK

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

21 Jun 2017, 01:11

Any ideas? To log into YouTube directly, probably via COM, without using a web browser? To replace that bit of script I have highlighted above.

Once logged in, it is easy to do everything with the API via COM and JSON strings.

(I have had this problem for a very long time, and spent a lot of time on this. #years.) Thanks for reading.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jNizM
Posts: 2400
Joined: 30 Sep 2013, 01:33
GitHub: jNizM
Contact:

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

21 Jun 2017, 03:08

jeeswg wrote:spent a lot of time on this. #years.
Than you should change your priorities in your life ;)
[AHK] 1.1.30.01 x64 Unicode | [WIN] 10 Pro (Version 1803) x64 | [GitHub] Profile
Donations are appreciated if I could help you
User avatar
jeeswg
Posts: 6125
Joined: 19 Dec 2016, 01:58
Location: UK

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

21 Jun 2017, 04:52

@jNizM, have you fixed the problem yet? I hear you're pretty foxy when it comes to solving problems.
I have had this problem for a very long time ... #years.
I might try and install BoBo's suggested utility, and/or improve my login procedure which uses Internet Explorer. I would be interested in any other freeware for doing things like creating/adding to/editing playlists etc, and uploading videos. Of course it would have to do the logging in for you, i.e. just a straightforward command-line tool.

I might release some of my YouTube API functions within the next few months, I've been replacing some fiddly string handling with the use of Coco's JSON library, which will make it easier for others to understand, use and amend my functions, and to use them as a template for further functions.

Part of the problem is that ClientLogin was only deprecated 2 years ago, and IT problems typically take 5 years to emerge and become properly documented/discussed, and then a further 5 years to be fully resolved in multiple programming languages with good code examples.

==================================================

These links sum up the problem, there appear to be dozens of questions on Stack Overflow discussing the issue or consequences of it:

Migrating from YouTube ClientLogin to OAuth 2.0 - Stack Overflow
https://stackoverflow.com/questions/104 ... -oauth-2-0

java - YouTube Data API: OAuth Authentication for services using V3 without user intervention - Stack Overflow
https://stackoverflow.com/questions/210 ... 9#21100089

Move from ClientLogin to OAuth 2.0 | YouTube Data API | Google Developers
https://developers.google.com/youtube/v ... standalone
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
malcev
Posts: 159
Joined: 12 Aug 2014, 12:37

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

21 Jul 2018, 06:14

You can login to Your Gmail like this:

Code: Select all

email := "email"
password := "password"

HTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
url := "https://accounts.google.com/ServiceLogin?continue=https%3A%2F%2Faccounts.google.com%2FManageAccount&rip=1&nojavascript=1&hl=en"
HTTP.Open("GET", url, true)
HTTP.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko)")
HTTP.SetRequestHeader("Pragma", "no-cache")
HTTP.SetRequestHeader("Cache-Control", "no-cache, no-store")
HTTP.SetRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT")
HTTP.Send()
HTTP.WaitForResponse()
RegExMatch(HTTP.ResponseText, "s)name=""gxf"".+?value=""(.+?)"".+?name=""SessionState"".+?value=""(.+?)""", match)
gxf := match1, SessionState := match2
data := "Page=PasswordSeparationSignIn&=&gxf=" gxf "&continue=https%3A%2F%2Faccounts.google.com%2FManageAccount&hl=en&rip=1&ProfileInformation=&SessionState=" SessionState "&flowName=GlifWebSignIn&_utf8=%E2%98%83&bgresponse=js_disabled&pstMsg=0&checkConnection=&checkedDomains=youtube&Email=" email "&identifiertoken=&identifiertoken_audio=&identifier-captcha-input=&signIn=Next&Passwd=&PersistentCookie=yes&rmShown=1"

url := "https://accounts.google.com/signin/v1/lookup"
HTTP.Open("POST", url, true)
HTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
HTTP.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko)")
HTTP.SetRequestHeader("Pragma", "no-cache")
HTTP.SetRequestHeader("Cache-Control", "no-cache, no-store")
HTTP.SetRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT")
HTTP.Send(data)
HTTP.WaitForResponse()
RegExMatch(HTTP.ResponseText, "s)name=""GALX"".+?value=""(.+?)"".+?name=""gxf"".+?value=""(.+?)"".+?name=""ProfileInformation"".+?value=""(.+?)"".+?name=""SessionState"".+?value=""(.+?)""", match)
GALX := match1, gxf := match2, ProfileInformation := match3, SessionState := match4
data := "Page=PasswordSeparationSignIn&GALX=" GALX "&gxf=" gxf "&continue=https%3A%2F%2Faccounts.google.com%2FManageAccount&hl=en&checkedDomains=youtube&checkConnection=&pstMsg=0&rip=1&flowName=GlifWebSignIn&ProfileInformation=" ProfileInformation "&SessionState=" SessionState "&_utf8=%E2%98%83&bgresponse=js_disabled&pstMsg=0&checkConnection=&checkedDomains=youtube&Email=" email "&Passwd=" password "&signIn=Sign+in&PersistentCookie=yes&rmShown=1"

url := "https://accounts.google.com/signin/challenge/sl/password"
HTTP.Open("POST", url, true)
HTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
HTTP.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko)")
HTTP.SetRequestHeader("Pragma", "no-cache")
HTTP.SetRequestHeader("Cache-Control", "no-cache, no-store")
HTTP.SetRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT")
HTTP.Send(data)
HTTP.WaitForResponse()
msgbox % HTTP.ResponseText
User avatar
jeeswg
Posts: 6125
Joined: 19 Dec 2016, 01:58
Location: UK

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

15 Aug 2018, 07:58

- Wow thanks so much. By saving the html that is produced at the end, and creating a file, it appears to confirm that the login works.
- Btw if you log in like this, it appears that you can't access Gmail/YouTube via Internet Explorer. It would be interesting if that were possible somehow.
- Since I can't manually log out of Gmail/YouTube, via Internet Explorer or another web browser, I'd need to log out programmatically. Do you have a script to do that? (Does it log out when the script ends?)
- How did you figure out this script? Thanks.

- Btw when I first tried to log in I got this error:
Verify it's you

This device isn't recognized. For your security, Google wants to make sure it's really you. Learn more
- Although the login appears to work, I haven't managed to get the login and then a YouTube playlist edit to occur. I will post my code for editing a YouTube playlist at some point, to see if anyone can marry the two together.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
malcev
Posts: 159
Joined: 12 Aug 2014, 12:37

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

15 Aug 2018, 11:14

Btw if you log in like this, it appears that you can't access Gmail/YouTube via Internet Explorer. It would be interesting if that were possible somehow.
It is possible through Msxml2.XMLHTTP because it uses the same cookies as IE.
Since I can't manually log out of Gmail/YouTube, via Internet Explorer or another web browser, I'd need to log out programmatically. Do you have a script to do that? (Does it log out when the script ends?)

Code: Select all

url := "https://accounts.google.com/Logout"
HTTP.Open("GET", url, true)
HTTP.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko)")
HTTP.SetRequestHeader("Pragma", "no-cache")
HTTP.SetRequestHeader("Cache-Control", "no-cache, no-store")
HTTP.SetRequestHeader("If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT")
HTTP.Send()
HTTP.WaitForResponse()
msgbox % HTTP.ResponseText
How did you figure out this script
I turn off javascript in IE.
Turn off all buttons in Network tab in developer tools and start recording.
Then I login to my google account, stop recording and examine what I send to server and what receive.
User avatar
jeeswg
Posts: 6125
Joined: 19 Dec 2016, 01:58
Location: UK

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

15 Aug 2018, 14:39

This is really interesting Malcev, thanks very much for the information. I will investigate further.
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
User avatar
jeeswg
Posts: 6125
Joined: 19 Dec 2016, 01:58
Location: UK

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

13 Feb 2019, 12:22

- malcev's script above no longer works because Google changed how they handle security.
- I decided to try and automate logging in and updating a playlist via Internet Explorer.
- Note: it can be tricky to get scripts that interact with web elements working perfectly, and the websites can change at any time.
- Try small tasks with the YouTube API first, before moving on to bigger tasks.

Code: Select all

;YouTube add item to playlist

;key user info to fill in:

vGblYouTubeApiKey := "#######################################"
vGblYouTubeClientID := "########################################################################"
vGblYouTubeClientSecret := "########################"
vChannelID := "########################"

vName := "####################" ;for Gmail login
vPW := "####################" ;for Gmail login
vChannelTitle := "####################" ;for 'Choose your account' login

vPlaylistID := "##################################"
vVideoID := "###########"
;vPlaylistPos := 7 ;1-based index (or -1 to add to end)
vPlaylistPos := -1 ;1-based index (or -1 to add to end)
return

;==================================================

#NoEnv
AutoTrim, Off
SetBatchLines, -1

#SingleInstance force
#KeyHistory 0
#UseHook
#Warn
ListLines, Off
Menu, Tray, Click, 1
Menu, Tray, Tip, % RegExReplace(A_ScriptName, "\.[^.]*$")

;==================================================

q:: ;youtube add item to playlist - log in to gmail
WinGet, hWnd, ID, ahk_class IEFrame
oWB := WBGet("ahk_id " hWnd)
vUrl := "https://accounts.google.com/signin/v2/identifier?continue=https%3A%2F%2Fmail.google.com%2Fmail%2F&service=mail&sacu=1&rip=1&flowName=GlifWebSignIn&flowEntry=ServiceLogin"
oWB.Navigate(vUrl)
while oWB.busy || oWB.readyState!=4 || oWB.document.readyState!="complete" ;READYSTATE_COMPLETE := 4
	Sleep, 10
oWB.document.getElementById("identifierId").value := vName
while !(oWB.document.getElementById("identifierId").value = vName)
	Sleep, 10
oElts := oWB.document.getElementsByTagName("span")
Loop, % oElts.length
{
	oElt := oElts[A_Index-1]
	if InStr(oElt.innerText, "Next")
	{
		oElt.click()
		break
	}
}
while oWB.busy || oWB.readyState!=4 || oWB.document.readyState!="complete" ;READYSTATE_COMPLETE := 4
	Sleep, 10
while !oWB.document.getElementsByName("password").length
	Sleep, 10
oWB.document.getElementsByName("password").0.value := vPW
Sleep, 500
oElts := oWB.document.getElementsByTagName("span")
Loop, % oElts.length
{
	oElt := oElts[A_Index-1]
	if InStr(oElt.innerText, "Next")
	{
		oElt.click()
		break
	}
}
oWB := ""
return

;==================================================

w:: ;youtube add item to playlist - update playlist
WinGet, hWnd, ID, ahk_class IEFrame
oWB := WBGet("ahk_id " hWnd)
vRedirectUri := "http://localhost"
vUrl1 := "https://accounts.google.com/o/oauth2/auth?client_id=" vGblYouTubeClientID "&redirect_uri=" vRedirectUri "&scope=https://gdata.youtube.com&response_type=code&access_type=offline"
oWB.Navigate(vUrl1)
;MsgBox
while oWB.busy || oWB.readyState!=4 || oWB.document.readyState!="complete" ;READYSTATE_COMPLETE := 4
	Sleep, 10
oElts := oWB.document.getElementsByTagName("div")

;click on account:
;Choose your account or a brand account
;to continue to Product name
;MsgBox, % vChannelTitle
Loop, % oElts.length
{
	oElt := oElts[A_Index-1]
	if (oElt.innerText = vChannelTitle)
	{
		oElt.click()
		break
	}
}
while oWB.busy || oWB.readyState!=4 || oWB.document.readyState!="complete" ;READYSTATE_COMPLETE := 4
	Sleep, 10
Loop, 100
{
	vUrlTemp := oWB.document.url
	if InStr(vUrlTemp, "http://localhost/?code=")
		break
	Sleep, 100
}

if !InStr(vUrlTemp, "http://localhost/?code=")
{
	MsgBox, % "error"
	return
}
vText := oWB.document.url
vPos := InStr(vText, "=")
vAuthCode := SubStr(vText, vPos+1)
;MsgBox, % vAuthCode

vUrl2 := "https://accounts.google.com/o/oauth2/token"
oHTTP1 := ComObjCreate("WinHttp.WinHttpRequest.5.1")
oHTTP1.Open("POST", vUrl2, False)
oHTTP1.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
oHTTP1.SetRequestHeader("Host", "accounts.google.com")

vPostData1 = ;continuation section
(Join
code=%vAuthCode%&
client_id=%vGblYouTubeClientID%&
client_secret=%vGblYouTubeClientSecret%&
redirect_uri=%vRedirectUri%&
grant_type=authorization_code
)

oHTTP1.Send(vPostData1)
vText := oHTTP1.ResponseText
;MsgBox, % vText

vAccessToken := ""
Loop, Parse, vText, `n, `r
{
	if A_LoopField contains "access_token"
		vPos1 := InStr(A_LoopField, ":"), vPos2 := JEE_InStr(A_LoopField, Chr(34), 0, -1), vAccessToken := SubStr(A_LoopField, vPos1+3, vPos2-vPos1-3)
}

vPlaylistPos0 := vPlaylistPos - 1 ;convert to 0-based index
vUrl3 := "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&key=" vGblYouTubeApiKey "&access_token=" vAccessToken
if !(vPlaylistPos0 = -2) ;add to specific position
{
	vPostData2 = ;continuation section
	(LTrim
	{
	  "snippet": {
	    "playlistId": "%vPlaylistID%",
	    "position": %vPlaylistPos0%,
	    "resourceId": {
	      "kind": "youtube#video",
	      "videoId": "%vVideoID%"
	    }
	  }
	}
	)
}
else if (vPlaylistPos0 = -2) ;add to end
{
	vPostData2 = ;continuation section
	(LTrim
	{
	  "snippet": {
	    "playlistId": "%vPlaylistID%",
	    "resourceId": {
	      "kind": "youtube#video",
	      "videoId": "%vVideoID%"
	    }
	  }
	}
	)
}

oHTTP2 := ComObjCreate("WinHttp.WinHttpRequest.5.1")
oHTTP2.Open("POST", vUrl3 , False)
oHTTP2.SetRequestHeader("Content-Type", "application/json")
oHTTP2.Send(vPostData2)
vText := oHTTP2.ResponseText
;MsgBox, % Clipboard := vText
oWB.Navigate("https://www.youtube.com/channel/" vChannelID)
MsgBox, % "done"
return

;==================================================

;[WBGet function for AHK v1.1]
;WBGet function - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=6&t=39869

WBGet(WinTitle="ahk_class IEFrame", Svr#=1) {               ;// based on ComObjQuery docs
   static msg := DllCall("RegisterWindowMessage", "str", "WM_HTML_GETOBJECT")
        , IID := "{0002DF05-0000-0000-C000-000000000046}"   ;// IID_IWebBrowserApp
;//     , IID := "{332C4427-26CB-11D0-B483-00C04FD90119}"   ;// IID_IHTMLWindow2
   SendMessage msg, 0, 0, Internet Explorer_Server%Svr#%, %WinTitle%
   if (ErrorLevel != "FAIL") {
      lResult:=ErrorLevel, VarSetCapacity(GUID,16,0)
      if DllCall("ole32\CLSIDFromString", "wstr","{332C4425-26CB-11D0-B483-00C04FD90119}", "ptr",&GUID) >= 0 {
         DllCall("oleacc\ObjectFromLresult", "ptr",lResult, "ptr",&GUID, "ptr",0, "ptr*",pdoc)
         return ComObj(9,ComObjQuery(pdoc,IID,IID),1), ObjRelease(pdoc)
      }
   }
}

;==================================================

;works like InStr (AHK v2) (on AHK v1/v2)
JEE_InStr(ByRef vText, vNeedle, vCaseSen:=0, vPos:=1, vOcc:=1)
{
	local
	static vIsV1 := InStr(1, 1,, 0)
	if (vPos = 0)
		return
	if vIsV1 && (vPos <= -1)
		vPos++
	return InStr(vText, vNeedle, vCaseSen, vPos, vOcc)
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
malcev
Posts: 159
Joined: 12 Aug 2014, 12:37

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

13 Feb 2019, 18:44

You can login with OAuth 2.0.
You have to register your application to connect to Youtube API.
Go to https://console.developers.google.com
Create new project
Choose Your project
Go to Dashboard, click "Enable Apis and Services".
Find YouTube Data API v3, click on it, then press enable.
Click on the right - Create Credentials -> Api Key
Click on "API key" in phrase: "If you wish you can skip this step and create an API key, client ID, or service account".
Press "Create".
You have got API key for Youtube, like this: "AIzaS9999999999999nm4rYaTA3EC397_ik"
Press OAuth Consent Screen, enter any name in Application name, press save.
Click Create Credentials -> OAuth Client ID -> Other -> Create
Download JSon.
Json will be like this:

Code: Select all

{"installed":{"client_id":"341870872-dgbbg66.apps.googleusercontent.com","project_id":"certain-hau-29","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"JJN1OS7110v-j","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}}
You need client_id and client_secret from this Json.
Now You have to allow access of our device.
Create code with Your data:

Code: Select all

; change data
ClientId := "341870872-dgbbg66.apps.googleusercontent.com"
ClientSecret := "JJN1OS7110v-j"

; do not change data
RedirectUri := "http://localhost"
scope := "https://www.googleapis.com/auth/youtube"
msgbox % clipboard := "https://accounts.google.com/o/oauth2/auth?redirect_uri=" UriEncode(RedirectUri) "&response_type=code&client_id=" ClientId "&scope=" UriEncode(scope) "&clientSecret=" ClientSecret "&approval_prompt=force"

UriEncode(Uri)
{
   VarSetCapacity(Var, StrPut(Uri, "UTF-8"), 0)
   StrPut(Uri, &Var, "UTF-8")
   f := A_FormatInteger
   SetFormat, IntegerFast, H
   While Code := NumGet(Var, A_Index - 1, "UChar")
      If (Code >= 0x30 && Code <= 0x39 ; 0-9
         || Code >= 0x41 && Code <= 0x5A ; A-Z
         || Code >= 0x61 && Code <= 0x7A) ; a-z
         Res .= Chr(Code)
      Else
         Res .= "%" . SubStr(Code + 0x100, -1)
   SetFormat, IntegerFast, %f%
   Return, Res
}
Run it, paste and go this link to browser.
Allow to use our device and at the end You will be redirected to 404 page.
Copy from address bar part of link http://localhost/?code=4/7wCaeBV61IBslGg5pU9AZZ-XvrtJp7Dmnfh_khOlv6Co-sUuL0OMd7jt5s&scope.
It is our Authorization.
Create and run the next script

Code: Select all

; change data
ClientId := "341870872-dgbbg66.apps.googleusercontent.com"
ClientSecret := "JJN1OS7110v-j"
AuthorizationCode := "4/7wCaeBV61IBslGg5pU9AZZ-XvrtJp7Dmnfh_khOlv6Co-sUuL0OMd7jt5s"

; do not change data
RedirectUri := "http://localhost"
Request := "grant_type=authorization_code&code=" AuthorizationCode "&redirect_uri=" UriEncode(RedirectUri) "&client_id=" ClientId "&client_secret=" ClientSecret
WinHTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
WinHTTP.Open("POST", "https://accounts.google.com/o/oauth2/token", true)
WinHTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
WinHTTP.Send(Request)
WinHTTP.WaitForResponse()
msgbox % clipboard := WinHTTP.ResponseText

UriEncode(Uri)
{
   VarSetCapacity(Var, StrPut(Uri, "UTF-8"), 0)
   StrPut(Uri, &Var, "UTF-8")
   f := A_FormatInteger
   SetFormat, IntegerFast, H
   While Code := NumGet(Var, A_Index - 1, "UChar")
      If (Code >= 0x30 && Code <= 0x39 ; 0-9
         || Code >= 0x41 && Code <= 0x5A ; A-Z
         || Code >= 0x61 && Code <= 0x7A) ; a-z
         Res .= Chr(Code)
      Else
         Res .= "%" . SubStr(Code + 0x100, -1)
   SetFormat, IntegerFast, %f%
   Return, Res
}
Answer will be with access_token and refresh_token:

Code: Select all

{
  "access_token": "ya29.GluuBhDBPs66aSxf3TR0k7VetBWezYD_GXBcyKIBMIIOjWkkfsPcNDDh4pVCXy5U4C-g3Y1GxxF_26",
  "expires_in": 3600,
  "refresh_token": "1/SjYrJK9Ja2N57TxxIdoxvMTrrj2vDReeFK7yMOTLgA6fLsJp",
  "scope": "https://www.googleapis.com/auth/youtube",
  "token_type": "Bearer"
}
Now You can connect to Youtube Api and add file to Your playlist:

Code: Select all

; change data
videoId := "A_QfO7g1YsI"
playlistId := "PLp9-Tn608wZdTS44vpp9hYhke"
YoutubeApiKey := "AIzaS9999999999999nm4rYaTA3EC397_ik"
access_token := "ya29.GluuBhDBPs66aSxf3TR0k7VetBWezYD_GXBcyKIBMIIOjWkkfsPcNDDh4pVCXy5U4C-g3Y1GxxF_26"
refresh_token := "1/SjYrJK9Ja2N57TxxIdoxvMTrrj2vDReeFK7yMOTLgA6fLsJp"

; do not change data
url := "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&key=" YoutubeApiKey
Json = {'snippet': {'playlistId': '%playlistId%', 'resourceId': {'kind': 'youtube#video', 'videoId': '%videoId%'}}}

WinHTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
WinHTTP.Open("POST", url, true)
WinHTTP.SetRequestHeader("Authorization", "Bearer " access_token)
WinHTTP.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
WinHTTP.SetRequestHeader("Content-Type", "application/json")
WinHTTP.Send(Json)
WinHTTP.WaitForResponse()
msgbox % WinHTTP.ResponseText
To refresh token You have to send this request:

Code: Select all

; change data
ClientId := "341870872-dgbbg66.apps.googleusercontent.com"
ClientSecret := "JJN1OS7110v-j"
RefreshToken := "1/SjYrJK9Ja2N57TxxIdoxvMTrrj2vDReeFK7yMOTLgA6fLsJp"

; do not change data
Request := "refresh_token=" RefreshToken "&client_id=" ClientId "&client_secret=" ClientSecret "&grant_type=refresh_token"
WinHTTP := ComObjCreate("WinHTTP.WinHTTPRequest.5.1")
WinHTTP.Open("POST", "https://accounts.google.com/o/oauth2/token", true)
WinHTTP.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
WinHTTP.Send(Request)
WinHTTP.WaitForResponse()
msgbox % clipboard := WinHTTP.ResponseText
User avatar
Tigerlily
Posts: 277
Joined: 04 Oct 2018, 22:31

Re: log in to Gmail/YouTube programmatically (+ YouTube API)

14 Feb 2019, 04:25

This is awesome and just what I've been looking for!! - thanks jesswg for posting & thanks malcev for such a great solution. :bravo:


jesswg, thought some code I've been working on may be helpful as well. You can set it this way to be able to log in and out of IE as you see fit, may require a bit of tweaking. Possibly mix it with malcev's solution.

I've been pulling data from Google Search Console straight into Excel in one script using cURL via Command Prompt, Some InputBoxes & and Coco's JXON parser, but it's the same sort of thing.

This is a snippet of the basic idea:

Code: Select all

; ***REQUEST AUTHORIZATION:               Take out ".readonly" to allow edits, this is here for safety so no one writes over valuable data on accident

; You will need to provide Client ID, Client Secret, and Scope below - it's hardcoded in here, but you can make use variables if wanted.
; The examples of Client ID & Secret below were randomly generated, but match the same "necessary format".
; You will receive the authorization code and copy/paste it into the InputBox.

Run, https://accounts.google.com/o/oauth2/auth?client_id=9418482231598-euxmo988t5pa45a62n33mo1sdn71k3m.apps.googleusercontent.com&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/webmasters.readonly&response_type=code

SoundPlay *-1	
Gui +LastFound +OwnDialogs +AlwaysOnTop
InputBox, AuthCode, Give Authorization, Log in with the brand1 credentials`, then copy and paste the authorization code that is given here and press okay.`n`nPress cancel to close this program.
if ErrorLevel
	{
		MsgBox, 0x1030, GSC Scraper is Shutting Down, GSC Scraper will now close.
		
		ExitApp
	}

; ***GET ACCESS TOKEN: - Pass Authorization Code to receive Access Token

RunWait, %comspec% /k curl -d code=%AuthCode% -d client_id=9518468643198-euxmo93688t5pa45a62n3o1sdn71k3m.apps.googleusercontent.com -d client_secret=-JiTw8K9zZpSpffpSpWLRaRTAC -d redirect_uri=urn:ietf:wg:oauth:2.0:oob -d grant_type=authorization_code https://www.googleapis.com/oauth2/v4/token > C:\users\username\accesstoken.json && exit ,,Hide ,CMDpid


; ***MANUALLY PROVIDE DATE RANGE THAT MATCHES NECESSARY FORMAT

TryAgainStartDateGSC:
FormatTime, TodayDateGSC, , yyyy-MM-dd

SoundPlay *-1	
Gui +LastFound +OwnDialogs +AlwaysOnTop
InputBox, StartDateGSC, Please provide Start Date, Please provide the start date of your data data range in YYYY-MM-DD format (e.g. 2018-01-03) and press okay.`n`nYou will provide the end date in the next window.`n`nPress cancel to close this program., , , , , , , , %TodayDateGSC%
if ErrorLevel
	{
		MsgBox, 0x1030, Please Provide a Date, Please provide a date - try again. Closing.
		
		ExitApp
	}



SoundPlay *-1	
Gui +LastFound +OwnDialogs +AlwaysOnTop
InputBox, EndDateGSC, Please provide End Date, Please provide the end date of your data data range in YYYY-MM-DD format (e.g. 2018-02-09) and press okay.`n`nPress cancel to close this program., , , , , , , , %TodayDateGSC%
if ErrorLevel
	{
		MsgBox, 0x1030, Please Provide a Date, Please provide a date - try again. Closing.

		ExitApp
	}


; ***HTTP REQUEST

	{
	FileRead, JSONdata, C:\users\username\accesstoken.json
	readJSON := Jxon_Load(JSONdata) ; load new 
							
							AccessToken := readJSON["access_token"] ; Access Token to Pass to API
	}

	RunWait, %comspec% /k curl -H "Authorization: Bearer %AccessToken%" -H "Content-Type: application/json" -d {\"startDate\":\"%StartDateGSC%\"`,\"endDate\":\"%EndDateGSC%\"} https://www.googleapis.com/webmasters/v3/sites/http`%3A`%2F`%2Fwww.brand1.com`%2FUS`%2F/searchAnalytics/query?fields=responseAggregationType`%2Crows > C:\users\username\gscHTTPdata.json && exit ,,Hide ,CMDpid

	RunWait, %comspec% /k curl -H "Authorization: Bearer %AccessToken%" -H "Content-Type: application/json" -d {\"startDate\":\"%StartDateGSC%\"`,\"endDate\":\"%EndDateGSC%\"} https://www.googleapis.com/webmasters/v3/sites/https`%3A`%2F`%2Fwww.brand1.com`%2FUS`%2Fen_US`%2F/searchAnalytics/query?fields=responseAggregationType`%2Crows > C:\users\username\gscHTTPSdata.json && exit,,Hide ,CMDpid ; brand1



;[][][][][][][][][][][][][][][][]				GSC Clicks Data to Excel Worksheet 			[][][][][][][][][][][][][][][][];



	{
	FileRead, JSONdata, C:\users\username\gscHTTPdata.json
	readJSONhttp := Jxon_Load(JSONdata) ; load new 
							
							wCLICK.Range(NewSiteData).Value := readJSONhttp["rows",1,"clicks"] ; HTTP Clicks						
					
	}
	

	{
	FileRead, JSONdata, C:\users\username\gscHTTPSdata.json
	readJSONhttps := Jxon_Load(JSONdata) ; load new 

							wCLICK.Range(OldSiteData).Value :=  readJSONhttps["rows",1,"clicks"] ; HTTPS Clicks	
	}	
	
MsgBox Finished
ExitApp


;[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][];
;[][][][][][][][]																							[][][][][][][][];
;[][][][][][][][]										FUNCTIONS								[][][][][][][][];
;[][][][][][][][]																							[][][][][][][][];
;[][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][];


;Coco's JXON_Load function: Google Search "Coco's JSON.ahk" and go to Authotkey.com forum post (https://autohotkey.com/boards/viewtopic.php?t=627) or Coco's Github (https://github.com/cocobelgica/AutoHotkey-JSON) for reference 

Jxon_Load(ByRef src, args*)
{
	static q := Chr(34)

	key := "", is_key := false
	stack := [ tree := [] ]
	is_arr := { (tree): 1 }
	next := q . "{[01234567890-tfn"
	pos := 0
	while ( (ch := SubStr(src, ++pos, 1)) != "" )
	{
		if InStr(" `t`n`r", ch)
			continue
		if !InStr(next, ch, true)
		{
			ln := ObjLength(StrSplit(SubStr(src, 1, pos), "`n"))
			col := pos - InStr(src, "`n",, -(StrLen(src)-pos+1))

			msg := Format("{}: line {} col {} (char {})"
			,   (next == "")      ? ["Extra data", ch := SubStr(src, pos)][1]
			  : (next == "'")     ? "Unterminated string starting at"
			  : (next == "\")     ? "Invalid \escape"
			  : (next == ":")     ? "Expecting ':' delimiter"
			  : (next == q)       ? "Expecting object key enclosed in double quotes"
			  : (next == q . "}") ? "Expecting object key enclosed in double quotes or object closing '}'"
			  : (next == ",}")    ? "Expecting ',' delimiter or object closing '}'"
			  : (next == ",]")    ? "Expecting ',' delimiter or array closing ']'"
			  : [ "Expecting JSON value(string, number, [true, false, null], object or array)"
			    , ch := SubStr(src, pos, (SubStr(src, pos)~="[\]\},\s]|$")-1) ][1]
			, ln, col, pos)

			throw Exception(msg, -1, ch)
		}

		is_array := is_arr[obj := stack[1]]

		if i := InStr("{[", ch)
		{
			val := (proto := args[i]) ? new proto : {}
			is_array? ObjPush(obj, val) : obj[key] := val
			ObjInsertAt(stack, 1, val)
			
			is_arr[val] := !(is_key := ch == "{")
			next := q . (is_key ? "}" : "{[]0123456789-tfn")
		}

		else if InStr("}]", ch)
		{
			ObjRemoveAt(stack, 1)
			next := stack[1]==tree ? "" : is_arr[stack[1]] ? ",]" : ",}"
		}

		else if InStr(",:", ch)
		{
			is_key := (!is_array && ch == ",")
			next := is_key ? q : q . "{[0123456789-tfn"
		}

		else ; string | number | true | false | null
		{
			if (ch == q) ; string
			{
				i := pos
				while i := InStr(src, q,, i+1)
				{
					val := StrReplace(SubStr(src, pos+1, i-pos-1), "\\", "\u005C")
					static end := A_AhkVersion<"2" ? 0 : -1
					if (SubStr(val, end) != "\")
						break
				}
				if !i ? (pos--, next := "'") : 0
					continue

				pos := i ; update pos

				  val := StrReplace(val,    "\/",  "/")
				, val := StrReplace(val, "\" . q,    q)
				, val := StrReplace(val,    "\b", "`b")
				, val := StrReplace(val,    "\f", "`f")
				, val := StrReplace(val,    "\n", "`n")
				, val := StrReplace(val,    "\r", "`r")
				, val := StrReplace(val,    "\t", "`t")

				i := 0
				while i := InStr(val, "\",, i+1)
				{
					if (SubStr(val, i+1, 1) != "u") ? (pos -= StrLen(SubStr(val, i)), next := "\") : 0
						continue 2

					; \uXXXX - JSON unicode escape sequence
					xxxx := Abs("0x" . SubStr(val, i+2, 4))
					if (A_IsUnicode || xxxx < 0x100)
						val := SubStr(val, 1, i-1) . Chr(xxxx) . SubStr(val, i+6)
				}

				if is_key
				{
					key := val, next := ":"
					continue
				}
			}

			else ; number | true | false | null
			{
				val := SubStr(src, pos, i := RegExMatch(src, "[\]\},\s]|$",, pos)-pos)
			
			; For numerical values, numerify integers and keep floats as is.
			; I'm not yet sure if I should numerify floats in v2.0-a ...
				static number := "number", integer := "integer"
				if val is %number%
				{
					if val is %integer%
						val += 0
				}
			; in v1.1, true,false,A_PtrSize,A_IsUnicode,A_Index,A_EventInfo,
			; SOMETIMES return strings due to certain optimizations. Since it
			; is just 'SOMETIMES', numerify to be consistent w/ v2.0-a
				else if (val == "true" || val == "false")
					val := %value% + 0
			; AHK_H has built-in null, can't do 'val := %value%' where value == "null"
			; as it would raise an exception in AHK_H(overriding built-in var)
				else if (val == "null")
					val := ""
			; any other values are invalid, continue to trigger error
				else if (pos--, next := "#")
					continue
				
				pos += i-1
			}
			
			is_array? ObjPush(obj, val) : obj[key] := val
			next := obj==tree ? "" : is_array ? ",]" : ",}"
		}
	}

	return tree[1]
}
Cheers.
-TL

Return to “Ask For Help”

Who is online

Users browsing this forum: Bing [Bot], Google [Bot], Odlanir and 205 guests