YouTube API functions

Post your working scripts, libraries and tools for AHK v1.1 and older
User avatar
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

YouTube API functions

20 Jul 2018, 14:33

- I had mentioned here about YouTube API functions:
log in to Gmail/YouTube programmatically (+ YouTube API) - AutoHotkey Community
https://autohotkey.com/boards/viewtopic.php?f=5&t=32410
- Someone reminded me about it, asking specifically about getting the duration from a YouTube video, and so I now present the complete YouTube API 'get' functions library.
- I may work on a few 'set' functions in future to create/modify playlists.

Requirements:
- You need a YouTube API key.
- You need the file 'JSON.ahk' from Coco's JSON library.
GitHub - cocobelgica/AutoHotkey-JSON: JSON module for AutoHotkey
https://github.com/cocobelgica/AutoHotkey-JSON
- Note: you do *not* need to log in to Gmail/YouTube since the functions get information, but do not set information.

Queries:
- I would very much welcome any suggestions on ways to reduce the overhead. To burden the YouTube servers as minimally as possible for each action. E.g. compare these:
vUrl := "https://www.googleapis.com/youtube/v3/v ... nippet&id=" vVideoID "&key=" vGblYouTubeApiKey
vUrl := "https://www.googleapis.com/youtube/v3/v ... nippet&id=" vVideoID "&fields=items/snippet&key=" vGblYouTubeApiKey
Using the latter retrieves less information, so is it less costly? Or alternatively, does it actually require more overhead, since some filtering may be needed.
- Also, you can get the IDs and titles for a playlist ID, in one API call, but AFAIK you cannot also get the durations without doing separate API calls for specific video IDs.
- In addition, 3 functions currently download information from YouTube pages, since the information does not appear to be directly available via the YouTube API:
JEE_NEWYouTubeChannelGetRelated [get 'Related channels']
JEE_NEWYouTubeChannelGetUsername [although it does use the API to check the result]
JEE_NEWYouTubePlaylistGetViewCount

- Do notify of any issues.
- The functions have been tested, but not extensively. They have been updated within the last few days to use reliable JSON handling, over unreliable and less maintainable string manipulation. Also, various functions have been created and restructured to add greater consistency.
- 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
jeeswg
Posts: 6902
Joined: 19 Dec 2016, 01:58
Location: UK

Re: YouTube API functions

22 Jul 2018, 17:07

Library:

Code: Select all

JEEYouTubeAPI_Load()
{
}

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

;note: functions that do not use the YouTube API:
;JEE_YouTubeChannelGetRelated
;JEE_YouTubeChannelGetUsername [it does use the API to check the result]
;JEE_YouTubePlaylistGetViewCount

;note: for some functions you can specify separators like so:
;vSep := "" ;no delimiters (an array is returned) (although some functions always return a string)
;vSep := "`t" ;horizontal delimiter (an array is returned) (although some functions always return a string)
;vSep := ["`t", "`r`n"] ;horizontal and vertical delimiter (a string is returned)

;functions needed:
;FormatTime (AHK v2 functions for AHK v1)
;JEE_InStr
;JEE_ObjHasValSimple
;JEE_StrJoin
;JEE_SubStr

;libraries needed:
;JSON.ahk
;GitHub - cocobelgica/AutoHotkey-JSON: JSON module for AutoHotkey
;https://github.com/cocobelgica/AutoHotkey-JSON
;#Include %A_ScriptDir%\Lib\JSON.ahk

;functions:
;JEE_YouTubeChannelGetAvatarAndBannerUrls(vChannelID, ByRef vUrlA, ByRef vUrlB, oType:="")
;JEE_YouTubeChannelGetDate(vChannelID, vFormat:="yyyy-MM-dd HH:mm:ss")
;JEE_YouTubeChannelGetDescription(vChannelID)
;JEE_YouTubeChannelGetFeatIDs(vChannelID)
;JEE_YouTubeChannelGetPlaylistIDs(vChannelID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubeChannelGetPlaylistIDsAndTitles(vChannelID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubeChannelGetPlaylistIDsAndMult(vChannelID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubeChannelGetPlaylistIDsAndMult2(vChannelID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubeChannelGetRelated(vChannelID)
;JEE_YouTubeChannelGetStats(vChannelID, ByRef vCountV, ByRef vCountC, ByRef vCountS, ByRef vCountH, ByRef vCountT)
;JEE_YouTubeChannelGetSubbedToIDs(vChannelID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubeChannelGetSubbedToIDsAndTitles(vChannelID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubeChannelGetSubCount(vChannelID)
;JEE_YouTubeChannelGetTitle(vChannelID)
;JEE_YouTubeChannelGetUploadsPlaylistID(vChannelID, vPfx:="")
;JEE_YouTubeChannelGetUsername(vChannelID)
;JEE_YouTubeChannelGetVidCount(vChannelID)
;JEE_YouTubeChannelGetVidIDs(vChannelID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubeChannelGetVidIDsAndTitles(vChannelID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubeChannelGetVidIDsAndMult(vChannelID, vPfx:="", vSep:="", vMax:="", vUnit:="hm")
;JEE_YouTubeChannelGetVidIDsRecent(vChannelID, vPfx:="", vSep:="", vMax:=50)
;JEE_YouTubeChannelGetVidIDsAndTitlesRecent(vChannelID, vPfx:="", vSep:="", vMax:=50)
;JEE_YouTubeChannelGetViewCount(vChannelID)
;JEE_YouTubeFormatDate(vDate, vFormat)
;JEE_YouTubeFormatDuration(vDuration, vUnit)
;JEE_YouTubePlaylistGetChannelID(vPlaylistID)
;JEE_YouTubePlaylistGetDate(vPlaylistID, vFormat:="yyyy-MM-dd HH:mm:ss")
;JEE_YouTubePlaylistGetDescription(vPlaylistID)
;JEE_YouTubePlaylistGetTitle(vPlaylistID)
;JEE_YouTubePlaylistGetTitleAndVidCount(vPlaylistID, vPfx:="", vSep:="")
;JEE_YouTubePlaylistGetVidCount(vPlaylistID)
;JEE_YouTubePlaylistGetVidIDs(vPlaylistID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubePlaylistGetVidIDsAndTitles(vPlaylistID, vPfx:="", vSep:="", vMax:="")
;JEE_YouTubePlaylistGetVidIDsAndMult(vPlaylistID, vPfx:="", vSep:="", vMax:="", vUnit:="hm")
;JEE_YouTubePlaylistGetViewCount(vPlaylistID, vOpt:="x")
;JEE_YouTubeSearchGetVidIDs(vNeedles, vPfx:="", vMax:=50)
;JEE_YouTubeSearchGetVidIDsAndTitles(vNeedles, vPfx:="", vSep:="", vMax:=50)
;JEE_YouTubeUserGetChannelID(vUsername)
;JEE_YouTubeVidGetAvail(vVideoID, vCountry)
;JEE_YouTubeVidGetChannelID(vVideoID)
;JEE_YouTubeVidGetDate(vVideoID, vFormat:="yyyy-MM-dd HH:mm:ss")
;JEE_YouTubeVidGetDescription(vVideoID)
;JEE_YouTubeVidGetDuration(vVideoID, vUnit:="hm")
;JEE_YouTubeVidGetJSON(vVideoID, vOpt:="x", vParts:="snippet,contentDetails,statistics,status")
;JEE_YouTubeVidGetRestrictedCountries(vVideoID)
;JEE_YouTubeVidGetStats(vVideoID, ByRef vCountV, ByRef vCountL, ByRef vCountD, ByRef vCountF, ByRef vCountC)
;JEE_YouTubeVidGetThumbUrl(vVideoID, oType:="")
;JEE_YouTubeVidGetTitle(vVideoID, vOpt:="")
;JEE_YouTubeVidGetViewCount(vVideoID)
;JEE_YouTubeVidIDIsValid(vVideoID)

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

;get images (channel avatar/banner)
JEE_YouTubeChannelGetAvatarAndBannerUrls(vChannelID, ByRef vUrlA, ByRef vUrlB, oType:="")
{
	global vGblYouTubeApiKey
	static oType2 := ["high", "medium", "default"]
	if !oType
		oType := oType2
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=snippet,brandingSettings&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)

	vUrlA := ""
	for vKey, vType in oType
		if ObjHasKey(oJSON.items.1.snippet.thumbnails, vType)
		{
			vUrlA := oJSON.items.1.snippet.thumbnails[vType].url
			break
		}

	if ObjHasKey(oJSON.items.1.brandingSettings.image, "bannerImageUrl")
		vUrlB := oJSON.items.1.brandingSettings.image.bannerImageUrl
	else
		vUrlB := ""
}

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

JEE_YouTubeChannelGetDate(vChannelID, vFormat:="yyyy-MM-dd HH:mm:ss")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=snippet&maxResults=50&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	vDate := oJSON.items.1.snippet.publishedAt
	return (vFormat = "") ? vDate : JEE_YouTubeFormatDate(vDate, vFormat)
}

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

JEE_YouTubeChannelGetDescription(vChannelID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=snippet&id=" vChannelID "&fields=items/snippet/description&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.snippet.description
}

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

;get 'Featured channels' IDs
JEE_YouTubeChannelGetFeatIDs(vChannelID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=brandingSettings&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	if ObjHasKey(oJSON.items.1.brandingSettings.channel, "featuredChannelsUrls")
		return oJSON.items.1.brandingSettings.channel.featuredChannelsUrls
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetPlaylistIDs(vChannelID, "", ["`t","`r`n"])

JEE_YouTubeChannelGetPlaylistIDs(vChannelID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=contentDetails&channelId=" vChannelID "&key=" vGblYouTubeApiKey "&maxResults=50"
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		Loop, % oJSON.items.Length()
			oArray.Push(vPfx oJSON.items[A_Index].id)
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetPlaylistIDsAndTitles(vChannelID, "", ["`t","`r`n"])

JEE_YouTubeChannelGetPlaylistIDsAndTitles(vChannelID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&channelId=" vChannelID "&key=" vGblYouTubeApiKey "&maxResults=50"
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		if (vSep = "")
		{
			Loop, % oJSON.items.Length()
				oArray.Push([vPfx oJSON.items[A_Index].id, oJSON.items[A_Index].snippet.title])
		}
		else
		{
			Loop, % oJSON.items.Length()
				oArray.Push(vPfx oJSON.items[A_Index].id vSep oJSON.items[A_Index].snippet.title)
		}
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetPlaylistIDsAndMult(vChannelID, "", ["`t","`r`n"])

;get IDs/title/vid count (but not view count)
;JEE_YouTubeChannelGetPlaylistIDsAndTitlesAndVidCounts
JEE_YouTubeChannelGetPlaylistIDsAndMult(vChannelID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&channelId=" vChannelID "&key=" vGblYouTubeApiKey "&maxResults=50"
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		if (vSep = "")
		{
			Loop, % oJSON.items.Length()
				oArray.Push([vPfx oJSON.items[A_Index].id, oJSON.items[A_Index].snippet.title, oJSON.items[A_Index].contentDetails.itemCount])
		}
		else
		{
			Loop, % oJSON.items.Length()
				oArray.Push(vPfx oJSON.items[A_Index].id vSep oJSON.items[A_Index].snippet.title vSep oJSON.items[A_Index].contentDetails.itemCount)
		}
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetPlaylistIDsAndMult2(vChannelID, "", ["`t","`r`n"])

;get IDs/title/vid count/view count
;note: to get the views requires downloading a webpage so can be slow
;JEE_YouTubeChannelGetPlaylistIDsAndTitlesAndVidCountsAndViews
JEE_YouTubeChannelGetPlaylistIDsAndMult2(vChannelID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&channelId=" vChannelID "&key=" vGblYouTubeApiKey "&maxResults=50"
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		if (vSep = "")
		{
			Loop, % oJSON.items.Length()
			{
				vViewCount := JEE_YouTubePlaylistGetViewCount(oJSON.items[A_Index].id)
				oArray.Push([vPfx oJSON.items[A_Index].id, oJSON.items[A_Index].snippet.title, oJSON.items[A_Index].contentDetails.itemCount], vViewCount)
			}
		}
		else
		{
			Loop, % oJSON.items.Length()
			{
				vViewCount := JEE_YouTubePlaylistGetViewCount(oJSON.items[A_Index].id)
				oArray.Push(vPfx oJSON.items[A_Index].id vSep oJSON.items[A_Index].snippet.title vSep oJSON.items[A_Index].contentDetails.itemCount vSep vViewCount)
			}
		}
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;get 'Related channels' ID/username urls
;note: this does not use the YouTube API to get the information
JEE_YouTubeChannelGetRelated(vChannelID)
{
	vUrl := "https://www.youtube.com/channel/" vChannelID
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	vText := oHTTP.ResponseText

	vNeedle1 := "<div class=" Chr(34) "branded-page-related-channels branded-page-box yt-card" Chr(34)
	vNeedle2 := "</ul>"
	vNeedle3 := Chr(34) "ux-thumb-wrap yt-uix-sessionlink" A_Space A_Space "spf-link " Chr(34)
	vNeedle4 := "href=" Chr(34)
	vNeedle5 := Chr(34)

	;vPos1 := InStr(vText, "Related channels")
	vPos1 := InStr(vText, vNeedle1, 0, 1, 2)
	vPos2 := InStr(vText, vNeedle2, 0, vPos1)
	vText := SubStr(vText, vPos1, vPos2-vPos1+1)

	oArray := []
	Loop, Parse, vText, <
	{
		vTemp := A_LoopField
		if !InStr(vTemp, vNeedle3)
			continue
		vPos4 := InStr(vTemp, vNeedle4)+6
		vPos5 := InStr(vTemp, vNeedle5, 0, vPos4)-1
		oArray.Push("https://www.youtube.com" SubStr(vTemp, vPos4, vPos5-vPos4+1))
	}
	return oArray
}

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

;view/comment/subscriber/hiddenSubscriber/video (video count as T for total)
JEE_YouTubeChannelGetStats(vChannelID, ByRef vCountV, ByRef vCountC, ByRef vCountS, ByRef vCountH, ByRef vCountT)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=statistics&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	for vKey, vValue in oJSON.items.1.statistics
	{
		vLetter := (vKey = "videoCount") ? "T" : SubStr(vKey, 1, 1)
		vCount%vLetter% := vValue
	}
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetSubbedToIDs(vChannelID, "https://www.youtube.com/channel/", ["`t","`r`n"])

;get a list of channels that the user is subscribed to (following)
;JEE_YouTubeChannelGetSubscriptions
JEE_YouTubeChannelGetSubbedToIDs(vChannelID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/subscriptions?part=snippet&maxResults=50&channelId=" vChannelID "&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		Loop, % oJSON.items.Length()
			oArray.Push(vPfx oJSON.items[A_Index].snippet.resourceId.channelId)
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetSubbedToIDsAndTitles(vChannelID, "https://www.youtube.com/channel/", ["`t","`r`n"])

;get a list of channels that the user is subscribed to (following)
JEE_YouTubeChannelGetSubbedToIDsAndTitles(vChannelID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/subscriptions?part=snippet&maxResults=50&channelId=" vChannelID "&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		if (vSep = "")
		{
			Loop, % oJSON.items.Length()
				oArray.Push([vPfx oJSON.items[A_Index].snippet.resourceId.channelId, oJSON.items[A_Index].snippet.title])
		}
		else
		{
			Loop, % oJSON.items.Length()
				oArray.Push(vPfx oJSON.items[A_Index].snippet.resourceId.channelId vSep oJSON.items[A_Index].snippet.title)
		}
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;get the number of subscribers (users that follow the channel)
JEE_YouTubeChannelGetSubCount(vChannelID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=statistics&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.statistics.subscriberCount
}

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

JEE_YouTubeChannelGetTitle(vChannelID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=snippet&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.snippet.title
}

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

JEE_YouTubeChannelGetUploadsPlaylistID(vChannelID, vPfx:="")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return vPfx oJSON.items.1.contentDetails.relatedPlaylists.uploads
}

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

;note: this does not use the YouTube API to get the information
;however, this does use the YouTube API to check the information
JEE_YouTubeChannelGetUsername(vChannelID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.youtube.com/channel/" vChannelID
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	vText := oHTTP.ResponseText

	vText := StrReplace(vText, "`r`n", " ")
	vText := StrReplace(vText, "`n", " ")
	vText := StrReplace(vText, Chr(34), " ")
	vText := StrReplace(vText, "{", " ")
	vText := StrReplace(vText, "}", " ")
	Loop, Parse, vText, % " "
	{
		vTemp := A_LoopField
		if (SubStr(vTemp, 1, 29) = "https://www.youtube.com/user/") || (SubStr(vTemp, 1, 28) = "http://www.youtube.com/user/")
		{
			vUsername := SubStr(vTemp, JEE_InStr(vTemp, "/", 0, -1)+1)
			break
		}
	}

	if (vUsername = "")
		return

	vChannelID2 := JEE_YouTubeUserGetChannelID(vUsername)
	if (vChannelID2 = vChannelID)
		return vUsername
}

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

JEE_YouTubeChannelGetVidCount(vChannelID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=statistics&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.statistics.videoCount
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetVidIDs(vChannelID, "https://www.youtube.com/watch?v=", ["`t","`r`n"])

;note: newer videos first
JEE_YouTubeChannelGetVidIDs(vChannelID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	vPlaylistID := oJSON.items.1.contentDetails.relatedPlaylists.uploads
	return JEE_YouTubePlaylistGetVidIDs(vPlaylistID, vPfx, vSep, vMax)
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetVidIDsAndTitles(vChannelID, "https://www.youtube.com/watch?v=", ["`t","`r`n"])

;note: newer videos first
JEE_YouTubeChannelGetVidIDsAndTitles(vChannelID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	vPlaylistID := oJSON.items.1.contentDetails.relatedPlaylists.uploads
	return JEE_YouTubePlaylistGetVidIDsAndTitles(vPlaylistID, vPfx, vSep, vMax)
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetVidIDsAndMult(vChannelID, "https://www.youtube.com/watch?v=", ["`t","`r`n"])

;note: newer videos first
;JEE_YouTubeChannelGetVidIDsAndTitlesAndDurations
JEE_YouTubeChannelGetVidIDsAndMult(vChannelID, vPfx:="", vSep:="", vMax:="", vUnit:="hm")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	vPlaylistID := oJSON.items.1.contentDetails.relatedPlaylists.uploads
	return JEE_YouTubePlaylistGetVidIDsAndMult(vPlaylistID, vPfx, vSep, vMax, vUnit)
}

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

;e.g. Clipboard := JEE_YouTubeChannelGetVidIDsRecent(vChannelID, "https://www.youtube.com/watch?v=", ["`t","`r`n"])

;not really necessary, use 'JEE_YouTubeChannelGetVidIDs' instead
;also, this function may omit more videos
JEE_YouTubeChannelGetVidIDsRecent(vChannelID, vPfx:="", vSep:="", vMax:=50)
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=" vChannelID "&maxResults=50&order=date&type=video&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		Loop, % oJSON.items.Length()
			oArray.Push(vPfx oJSON.items[A_Index].id.videoId)
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;not really necessary, use 'JEE_YouTubeChannelGetVidIDsAndTitles' instead
;also, this function may omit more videos
JEE_YouTubeChannelGetVidIDsAndTitlesRecent(vChannelID, vPfx:="", vSep:="", vMax:=50)
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=" vChannelID "&maxResults=50&order=date&type=video&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		if (vSep = "")
		{
			Loop, % oJSON.items.Length()
				oArray.Push([vPfx oJSON.items[A_Index].id.videoId, oJSON.items[A_Index].snippet.title])
		}
		else
		{
			Loop, % oJSON.items.Length()
				oArray.Push(vPfx oJSON.items[A_Index].id.videoId vSep oJSON.items[A_Index].snippet.title)
		}
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

JEE_YouTubeChannelGetViewCount(vChannelID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?part=statistics&id=" vChannelID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.statistics.viewCount
}

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

JEE_YouTubeFormatDate(vDate, vFormat)
{
	if (vFormat = "")
		return vDate
	else if (vFormat == "yyyy-MM-dd HH:mm:ss")
	{
		vDate := StrReplace(vDate, "T", " ")
		return SubStr(vDate, 1, -5)
	}
	else if (vFormat == "yyyyMMddHHmmss")
	{
		vDate := SubStr(vDate, 1, -5)
		return RegExReplace(vDate, "\D")
	}
	else
	{
		vDate := SubStr(vDate, 1, -5)
		vDate := RegExReplace(vDate, "\D")
		return FormatTime(vDate, vFormat)
	}
}

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

JEE_YouTubeFormatDuration(vDuration, vUnit)
{
	if (vUnit = "")
		return vDuration
	vDuration := RegExReplace(vDuration, "[A-Z]", ",$0,")
	vD := vH := vM := vS := 0
	Loop, Parse, vDuration, % ","
	{
		vTemp := A_LoopField
		if (vTemp = "W")
			vD := 7*vLast
		else if (vTemp = "D")
			vD += vLast
		else if (vTemp = "H")
			vH := vLast
		else if (vTemp = "M")
			vM := vLast
		else if (vTemp = "S")
			vS := vLast
		vLast := vTemp
	}
	if (vUnit = "")
		return vDuration
	else if InStr(vUnit, "s") && (vD+vH+vM = 0)
		return Format("{:02}", vS)
	else if InStr(vUnit, "m") && (vD+vH = 0)
		return Format("{:02}:{:02}", vM, vS)
	else if InStr(vUnit, "h") && (vD = 0)
		return Format("{:02}:{:02}:{:02}", vH, vM, vS)
	else if InStr(vUnit, "d")
		return Format("{:02}:{:02}:{:02}:{:02}", vD, vH, vM, vS)
	else if InStr(vUnit, "h")
		return Format("{:02}:{:02}:{:02}", vD*24+vH, vM, vS)
	else if InStr(vUnit, "m")
		return Format("{:02}:{:02}", vD*1440+vH*60+vM, vS)
	else if InStr(vUnit, "s")
		return Format("{:02}", vD*86400+vH*3600+vM*60+vS)
	else
		return vDuration
}

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

JEE_YouTubePlaylistGetChannelID(vPlaylistID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=" vPlaylistID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.snippet.channelId
}

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

JEE_YouTubePlaylistGetDate(vPlaylistID, vFormat:="yyyy-MM-dd HH:mm:ss")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=snippet&maxResults=50&id=" vPlaylistID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	vDate := oJSON.items.1.snippet.publishedAt
	return (vFormat = "") ? vDate : JEE_YouTubeFormatDate(vDate, vFormat)
}

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

JEE_YouTubePlaylistGetDescription(vPlaylistID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=" vPlaylistID "&fields=items/snippet/description&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.snippet.description
}

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

JEE_YouTubePlaylistGetTitle(vPlaylistID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=snippet&maxResults=50&id=" vPlaylistID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.snippet.title
}

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

; ;e.g.
; Clipboard := JEE_YouTubePlaylistGetTitleAndVidCount(vPlaylistID, "", "`t")

JEE_YouTubePlaylistGetTitleAndVidCount(vPlaylistID, vPfx:="", vSep:="")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=snippet,contentDetails&id=" vPlaylistID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	if (vSep = "")
		return [vPfx oJSON.items.1.snippet.title, oJSON.items.1.contentDetails.itemCount]
	else
		return vPfx oJSON.items.1.snippet.title vSep oJSON.items.1.contentDetails.itemCount
}

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

JEE_YouTubePlaylistGetVidCount(vPlaylistID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/playlists?part=contentDetails&maxResults=50&id=" vPlaylistID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.contentDetails.itemCount
}

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

;e.g. Clipboard := JEE_YouTubePlaylistGetVidIDs(vPlaylistID, "https://www.youtube.com/watch?v=", ["`t","`r`n"])

;note: newer videos first
JEE_YouTubePlaylistGetVidIDs(vPlaylistID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails&maxResults=50&playlistId=" vPlaylistID "&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		Loop, % oJSON.items.Length()
			oArray.Push(vPfx oJSON.items[A_Index].contentDetails.videoId)
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;e.g. Clipboard := JEE_YouTubePlaylistGetVidIDsAndTitles(vPlaylistID, "https://www.youtube.com/watch?v=", ["`t","`r`n"])

;note: newer videos first
JEE_YouTubePlaylistGetVidIDsAndTitles(vPlaylistID, vPfx:="", vSep:="", vMax:="")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=" vPlaylistID "&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		if (vSep = "")
		{
			Loop, % oJSON.items.Length()
				oArray.Push([vPfx oJSON.items[A_Index].snippet.resourceId.videoId, oJSON.items[A_Index].snippet.title])
		}
		else
		{
			Loop, % oJSON.items.Length()
				oArray.Push(vPfx oJSON.items[A_Index].snippet.resourceId.videoId vSep oJSON.items[A_Index].snippet.title)
		}
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;e.g. Clipboard := JEE_YouTubePlaylistGetVidIDsAndMult(vPlaylistID, "https://www.youtube.com/watch?v=", ["`t","`r`n"])

;note: newer videos first
;note: you can get the IDs and titles from a playlist
;but AFAIK you cannot also get the duration without doing a separate API call
;JEE_YouTubePlaylistGetVidIDsAndTitlesAndDurations
JEE_YouTubePlaylistGetVidIDsAndMult(vPlaylistID, vPfx:="", vSep:="", vMax:="", vUnit:="hm")
{
	global vGblYouTubeApiKey
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,contentDetails&maxResults=50&playlistId=" vPlaylistID "&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		if (vSep = "")
		{
			Loop, % oJSON.items.Length()
			{
				vDuration := JEE_YouTubeVidGetDuration(oJSON.items[A_Index].snippet.resourceId.videoId, vUnit)
				oArray.Push([vDuration, vPfx oJSON.items[A_Index].snippet.resourceId.videoId, oJSON.items[A_Index].snippet.title])
			}
		}
		else
		{
			Loop, % oJSON.items.Length()
			{
				vDuration := JEE_YouTubeVidGetDuration(oJSON.items[A_Index].snippet.resourceId.videoId, vUnit)
				oArray.Push(vDuration vSep vPfx oJSON.items[A_Index].snippet.resourceId.videoId vSep oJSON.items[A_Index].snippet.title)
			}
		}
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;note: this does not use the YouTube API to get the information
JEE_YouTubePlaylistGetViewCount(vPlaylistID, vOpt:="x")
{
	vUrl := "https://www.youtube.com/playlist?list=" vPlaylistID
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	vText := oHTTP.ResponseText
	if (vPos1 := InStr(vText, "videos</li><li>"))
		vPos1 += 15
	else if (vPos1 := InStr(vText, "1 video</li><li>"))
		vPos1 += 16
	else
		return
	if (vPos2 := InStr(vText, "</li><li>Updated "))
		vPos2--
	else if (vPos2 := InStr(vText, "</li><li>Last updated "))
		vPos2--
	else
		return

	vOutput := SubStr(vText, vPos1, vPos2-vPos1+1)
	if (vOpt = "x")
	{
		if (vOutput = "No views")
			vOutput := 0
		if (JEE_SubStr(vOutput, -5) = " view")
			vOutput := SubStr(vOutput, 1, -5)
		if (JEE_SubStr(vOutput, -6) = " views")
			vOutput := SubStr(vOutput, 1, -6)
		vOutput := StrReplace(vOutput, ",")
	}
	return vOutput
}

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

;note: vNeedles expects a comma-separated list
JEE_YouTubeSearchGetVidIDs(vNeedles, vPfx:="", vMax:=50)
{
	global vGblYouTubeApiKey
	vNeedles := Chr(34) vNeedles Chr(34)
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=50&q=" vNeedles "&type=video&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		Loop, % oJSON.items.Length()
			oArray.Push(vPfx oJSON.items[A_Index].id.videoId)
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

;note: vNeedles expects a comma-separated list
JEE_YouTubeSearchGetVidIDsAndTitles(vNeedles, vPfx:="", vSep:="", vMax:=50)
{
	global vGblYouTubeApiKey
	vNeedles := Chr(34) vNeedles Chr(34)
	oArray := []
	IsObject(vSep) && (vSep2 := vSep.2, vSep := vSep.1)
	Loop
	{
		vUrl := "https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=50&q=" vNeedles "&type=video&key=" vGblYouTubeApiKey
		if !(A_Index = 1)
			vUrl .= "&pageToken=" vToken
		oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
		oHTTP.Open("GET", vUrl)
		oHTTP.Send()
		oJSON := JSON.Load(oHTTP.ResponseText)
		if (vSep = "")
		{
			Loop, % oJSON.items.Length()
				oArray.Push([vPfx oJSON.items[A_Index].id.videoId, oJSON.items[A_Index].snippet.title])
		}
		else
		{
			Loop, % oJSON.items.Length()
				oArray.Push(vPfx oJSON.items[A_Index].id.videoId vSep oJSON.items[A_Index].snippet.title)
		}
		vToken := oJSON.nextPageToken
		if (vToken = "")
		|| (vMax && (A_Index*50 >= vMax))
			break
	}
	(vMax && oArray.Length() > vMax) && oArray.Delete(vMax+1, oArray.Length())
	if !(vSep2 = "")
		return JEE_StrJoin(vSep2, oArray*)
	return oArray
}

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

JEE_YouTubeUserGetChannelID(vUsername)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/channels?key=" vGblYouTubeApiKey "&forUsername=" vUsername "&part=id"
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.id
}

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

;e.g. vAvail := JEE_YouTubeVidGetAvail(vVideoID, "GB")
;e.g. vAvail := JEE_YouTubeVidGetAvail(vVideoID, "US")

;returns deleted/private/blocked/public/unlisted
;JEE_YouTubeVidGetPrivacyStatus(vUrl)
JEE_YouTubeVidGetAvail(vVideoID, vCountry)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=contentDetails,status&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	if !ObjHasKey(oJSON.items, 1)
		if oJSON.pageInfo.totalResults
			return "deleted"
		else
			return "private"
	if ObjHasKey(oJSON.items.1.contentDetails, "regionRestriction")
	&& JEE_ObjHasValSimple(oJSON.items.1.contentDetails.regionRestriction.blocked, vCountry)
		return "blocked"
	return oJSON.items.1.status.privacyStatus
}

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

JEE_YouTubeVidGetChannelID(vVideoID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.snippet.channelId
}

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

JEE_YouTubeVidGetDate(vVideoID, vFormat:="yyyy-MM-dd HH:mm:ss")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	vDate := oJSON.items.1.snippet.publishedAt
	return (vFormat = "") ? vDate : JEE_YouTubeFormatDate(vDate, vFormat)
}

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

JEE_YouTubeVidGetDescription(vVideoID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" vVideoID "&fields=items/snippet/description&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.snippet.description
}

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

; q:: ;YouTube video - get duration
; vGblYouTubeApiKey := "#######################################" ;39 chars
;
; vVideoID := "vioZf4TjoUI"
; vDuration1 := JEE_YouTubeVidGetDuration(vVideoID, "") ;PT4M21S
; vDuration2 := JEE_YouTubeVidGetDuration(vVideoID, "hm") ;04:21
; MsgBox, % vDuration1 "`r`n" vDuration2
;
; vVideoID := "04cF1m6Jxu8" ;P3W3DT20H31M21S
; vDuration1 := JEE_YouTubeVidGetDuration(vVideoID, "") ;P3W3DT20H31M21S
; vDuration2 := JEE_YouTubeVidGetDuration(vVideoID, "d") ;24:20:31:21
; vDuration2 := JEE_YouTubeVidGetDuration(vVideoID, "h") ;24:20:31:21 ;596:31:21
; MsgBox, % vDuration1 "`r`n" vDuration2
; return

;e.g. vDuration := JEE_YouTubeVidGetDuration("04cF1m6Jxu8", "") ;P3W3DT20H31M21S (3w 3d 20h 31m 21s)

;'hm' means max unit could be either h or m, so: HH:mm:ss or mm:ss
;'h' means max unit must be h, so: HH:mm:ss
;'d' means max unit must be d, so: dd:HH:mm:ss
;THE LONGEST VIDEO ON YOUTUBE - 596.5 HOURS - YouTube
;https://www.youtube.com/watch?v=04cF1m6Jxu8
JEE_YouTubeVidGetDuration(vVideoID, vUnit:="hm")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	vDuration := oJSON.items.1.contentDetails.duration
	return (vUnit = "") ? vDuration : JEE_YouTubeFormatDuration(vDuration, vUnit)
}

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

; ;e.g.
; vParts := "snippet,contentDetails,fileDetails,player,processingDetails,recordingDetails,statistics,status,suggestions,topicDetails"
; vParts := "snippet,contentDetails,player,recordingDetails,statistics,status,topicDetails" ;excluded: fileDetails,processingDetails,suggestions
; MsgBox, % Clipboard := JEE_YouTubeVidGetJSON(vVideoID, "x", vParts)

;YouTube Data API Overview  |  YouTube Data API  |  Google Developers
;https://developers.google.com/youtube/v3/getting-started#part
JEE_YouTubeVidGetJSON(vVideoID, vOpt:="x", vParts:="snippet,contentDetails,statistics,status")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=" vParts "&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	if (vOpt = "x")
		return StrReplace(oHTTP.ResponseText, "`n", "`r`n")
	return oHTTP.ResponseText
}

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

; ;e.g.
; oArray := JEE_YouTubeVidGetRestrictedCountries(vVideoID)
; MsgBox, % JEE_StrJoin(",", oArray*)

JEE_YouTubeVidGetRestrictedCountries(vVideoID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	if !ObjHasKey(oJSON.items.1.contentDetails, "regionRestriction")
		return
	return oJSON.items.1.contentDetails.regionRestriction.blocked
}

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

;counts: view/like/dislike/favorite/comment
JEE_YouTubeVidGetStats(vVideoID, ByRef vCountV, ByRef vCountL, ByRef vCountD, ByRef vCountF, ByRef vCountC)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=statistics&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	for vKey, vValue in oJSON.items.1.statistics
	{
		vLetter := SubStr(vKey, 1, 1)
		vCount%vLetter% := vValue
	}
}

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

;get image (video thumbnail)
JEE_YouTubeVidGetThumbUrl(vVideoID, oType:="")
{
	global vGblYouTubeApiKey
	static oType2 := ["maxres", "standard", "high", "medium", "default"]
	if !oType
		oType := oType2
	;vUrl := "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" vVideoID "&key=" vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" vVideoID "&fields=items/snippet&key=" vGblYouTubeApiKey ;omits unneeded data cf. url above
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)

	vUrl := ""
	for vKey, vType in oType
		if ObjHasKey(oJSON.items.1.snippet.thumbnails, vType)
		{
			vUrl := oJSON.items.1.snippet.thumbnails[vType].url
			break
		}
	return vUrl
}

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

JEE_YouTubeVidGetTitle(vVideoID, vOpt:="")
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=snippet&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	if !InStr(vOpt, "L")
		return oJSON.items.1.snippet.title
	else
		return oJSON.items.1.snippet.localized.title
}

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

JEE_YouTubeVidGetViewCount(vVideoID)
{
	global vGblYouTubeApiKey
	vUrl := "https://www.googleapis.com/youtube/v3/videos?part=statistics&id=" vVideoID "&key=" vGblYouTubeApiKey
	oHTTP := ComObjCreate("WinHttp.WinHttpRequest.5.1")
	oHTTP.Open("GET", vUrl)
	oHTTP.Send()
	oJSON := JSON.Load(oHTTP.ResponseText)
	return oJSON.items.1.statistics.viewCount
}

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

JEE_YouTubeVidIDIsValid(vVideoID)
{
	if !(StrLen(vVideoID) = 11)
		return 0
	return RegExMatch(vVideoID, "^[\w\-]{11}$")
}

;==================================================
Additional functions:

Code: Select all

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

;commands as functions (AHK v2 functions for AHK v1) - AutoHotkey Community
;https://autohotkey.com/boards/viewtopic.php?f=37&t=29689

FormatTime(YYYYMMDDHH24MISS:="", Format:="")
{
    local OutputVar
    FormatTime OutputVar, %YYYYMMDDHH24MISS%, %Format%
    return OutputVar
}

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

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

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

JEE_ObjHasValSimple(oArray, vValue2)
{
	for vKey, vValue in oArray
		if (vValue = vValue2)
			return 1
	return 0
}

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

; ;e.g.
; oArray := StrSplit("abcdefghijklmnopqrstuvwxyz")
; MsgBox, % JEE_StrJoin(" - ", oArray*)
; MsgBox, % JEE_StrJoin(["=","`r`n"], oArray*)
; MsgBox, % JEE_StrJoin(["`t","`r`n"], oArray*)
; MsgBox, % JEE_StrJoin(["`t","`t","`r`n"], oArray*)
; MsgBox, % JEE_StrJoin(["`t","`t","`t","`r`n"], oArray*)
; MsgBox, % JEE_StrJoin(["`t","`t","`t","`t","`r`n"], oArray*)
; MsgBox, % JEE_StrJoin(["","","","","`r`n"], oArray*)

JEE_StrJoin(vSep, oArray*)
{
	VarSetCapacity(vOutput, oArray.Length()*200*2)
	if IsObject(vSep) && (vSep.Length() = 1) ;convert 1-item array to string
		vSep := vSep.1
	if !IsObject(vSep)
	{
		Loop, % oArray.MaxIndex()-1
			vOutput .= oArray[A_Index] vSep
		vOutput .= oArray[oArray.MaxIndex()]
	}
	else
	{
		oSep := vSep, vCount := oSep.Length(), vIndex := 0
		Loop, % oArray.MaxIndex()-1
		{
			;vIndex := Mod(A_Index-1, vCount)+1
			vIndex := (vIndex = vCount) ? 1 : vIndex+1
			, vOutput .= oArray[A_Index] oSep[vIndex]
		}
		vOutput .= oArray[oArray.MaxIndex()]
	}
	return vOutput
}

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

;works like SubStr (AHK v2) (on AHK v1/v2)
JEE_SubStr(ByRef vText, vPos, vLen:="")
{
	static vIsV1 := !!SubStr(1, 0)
	if (vPos = 0)
		return
	if vIsV1 && (vPos <= -1)
		vPos++
	if (vLen = "")
		return SubStr(vText, vPos)
	return SubStr(vText, vPos, vLen)
}

;==================================================
homepage | tutorials | wish list | fun threads | donate
WARNING: copy your posts/messages before hitting Submit as you may lose them due to CAPTCHA
Marium0505
Posts: 39
Joined: 11 May 2020, 20:45

Re: YouTube API functions

19 Apr 2021, 09:10

Love this!

But have you looked into making it able to make changes? Such as create a playlist, add videos etc.?
User avatar
lmstearn
Posts: 688
Joined: 11 Aug 2016, 02:32
Contact:

Re: YouTube API functions

22 Aug 2023, 08:28

Here's a link for general Data API reference, and plenty of other stuff for YT players such as Playback status and Live Streams. It so occurred a certain language was omitted from their language samples repo, sure to change sometime soon. :P
Nigh time for a V2 port in any case.
:arrow: itros "ylbbub eht tuO kaerB" a ni kcuts m'I pleH
Marium0505
Posts: 39
Joined: 11 May 2020, 20:45

Re: YouTube API functions

22 Aug 2023, 09:14

Hey, thanks for the reply, forgot all about this thread.

I was finally able to make a working v1 class for Youtube's API a few months ago, will work on making it a v2 as soon as I'm done with my current projects. Might share one or more of them on Github and/or potentially here as well.

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 115 guests