Skip to content

Commit c6d9c7e

Browse files
committed
Re-working for V2, untested
1 parent 1610fb6 commit c6d9c7e

File tree

5 files changed

+158
-95
lines changed

5 files changed

+158
-95
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@
3030
.piolibdeps
3131
.clang_complete
3232
.gcc-flags.json
33+
.pio/
34+
.vscode/

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@ Currently the only implemented method is getting the channel statistics but it c
55

66
![Imgur](http://i.imgur.com/FmXyW4E.png)
77

8+
### Support what I do!
9+
10+
I have created a lot of different Arduino libraries that I hope people can make use of. [If you enjoy my work, please consider becoming a Github sponsor!](https://github.com/sponsors/witnessmenow/)
11+
812
## Getting a Google Apps API key (Required!)
913

1014
* Create an application [here](https://console.developers.google.com)
1115
* On the API Manager section, go to "Credentials" and create a new API key
1216
* Enable your application to communicate the YouTube Api [here](https://console.developers.google.com/apis/api/youtube)
1317
* Make sure the following URL works for you in your browser (Change the key at the end!):
14-
https://www.googleapis.com/youtube/v3/channels?part=statistics&id=UCu7_D0o48KbfhpEohoP7YSQ&key=PutYourNewlyGeneratedKeyHere
18+
https://www.googleapis.com/youtube/v3/channels?part=statistics&id=UCezJOfu7OtqGzd5xrP3q6WA&key=PutYourNewlyGeneratedKeyHere
1519

1620
## Installing
1721

18-
The downloaded code can be included as a new library into the IDE selecting the menu:
19-
20-
Sketch / include Library / Add .Zip library
22+
The easiest way to install this library is through the aduino library manager, just search for "Youtube"
2123

2224
You also have to install the ArduinoJson library written by [Benoît Blanchon](https://github.com/bblanchon). Search for it on the Arduino Library manager or get it from [here](https://github.com/bblanchon/ArduinoJson).
2325

library.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
name=YoutubeApi
2-
version=1.1.0
2+
version=2.0.0
33
author=Brian Lough
44
maintainer=Brian Lough <[email protected]>
55
sentence=A wrapper for the YouTube API for Arduino (supports ESP8266 & WiFi101 boards)
66
paragraph=Use this library to get YouTube channel statistics
77
category=Communication
88
url=https://github.com/witnessmenow/arduino-youtube-api
99
architectures=*
10+
depends=ArduinoJson

src/YoutubeApi.cpp

Lines changed: 130 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (c) 2017 Brian Lough. All right reserved.
2+
Copyright (c) 2020 Brian Lough. All right reserved.
33
44
YoutubeApi - An Arduino wrapper for the YouTube API
55
@@ -22,97 +22,143 @@
2222
#include "YoutubeApi.h"
2323

2424
YoutubeApi::YoutubeApi(String apiKey, Client &client) {
25+
int strLen = apiKey.length() + 1;
26+
char tempStr[strLen];
27+
apiKey.toCharArray(tempStr, strLen);
28+
29+
YoutubeApi(tempStr, client);
30+
}
31+
32+
YoutubeApi::YoutubeApi(char *apiKey, Client &client) {
2533
_apiKey = apiKey;
2634
this->client = &client;
2735
}
2836

29-
String YoutubeApi::sendGetToYoutube(String command) {
30-
String headers="";
31-
String body="";
32-
bool finishedHeaders = false;
33-
bool currentLineIsBlank = true;
34-
unsigned long now;
35-
bool avail;
36-
// Connect with youtube api over ssl
37-
if (client->connect(YTAPI_HOST, YTAPI_SSL_PORT)) {
38-
if(_debug) { Serial.println(".... connected to server"); }
39-
String a="";
40-
char c;
41-
int ch_count=0;
42-
client->println("GET " + command + "&key=" + _apiKey + " HTTP/1.1");
43-
client->print("HOST: ");
44-
client->println(YTAPI_HOST);
45-
client->println();
46-
now=millis();
47-
avail=false;
48-
while (millis() - now < YTAPI_TIMEOUT) {
49-
while (client->available()) {
50-
51-
// Allow body to be parsed before finishing
52-
avail = finishedHeaders;
53-
char c = client->read();
54-
//Serial.write(c);
55-
56-
if(!finishedHeaders){
57-
if (currentLineIsBlank && c == '\n') {
58-
finishedHeaders = true;
59-
}
60-
else{
61-
headers = headers + c;
62-
63-
}
64-
} else {
65-
if (ch_count < maxMessageLength) {
66-
body=body+c;
67-
ch_count++;
68-
}
69-
}
70-
71-
if (c == '\n') {
72-
currentLineIsBlank = true;
73-
}else if (c != '\r') {
74-
currentLineIsBlank = false;
75-
}
76-
}
77-
if (avail) {
78-
if(_debug) {
79-
Serial.println("Body:");
80-
Serial.println(body);
81-
Serial.println("END");
82-
}
83-
break;
84-
}
85-
}
86-
}
87-
closeClient();
88-
return body;
37+
int YoutubeApi::sendGetToYoutube(char *command) {
38+
client->flush();
39+
client->setTimeout(YTAPI_TIMEOUT);
40+
if (!client->connect(YTAPI_HOST, YTAPI_SSL_PORT))
41+
{
42+
Serial.println(F("Connection failed"));
43+
return false;
44+
}
45+
// give the esp a breather
46+
yield();
47+
48+
// Send HTTP request
49+
client->print(F("GET "));
50+
client->print(command);
51+
client->println(F(" HTTP/1.1"));
52+
53+
//Headers
54+
client->print(F("Host: "));
55+
client->println(YTAPI_HOST);
56+
57+
client->println(F("Cache-Control: no-cache"));
58+
59+
if (client->println() == 0)
60+
{
61+
Serial.println(F("Failed to send request"));
62+
return -1;
63+
}
64+
65+
int statusCode = getHttpStatusCode();
66+
67+
// Let the caller of this method parse the JSon from the client
68+
skipHeaders();
69+
return statusCode;
8970
}
9071

9172
bool YoutubeApi::getChannelStatistics(String channelId){
92-
String command="/youtube/v3/channels?part=statistics&id="+channelId; //If you can't find it(for example if you have a custom url) look here: https://www.youtube.com/account_advanced
93-
if(_debug) { Serial.println(F("Closing client")); }
94-
String response = sendGetToYoutube(command); //recieve reply from youtube
95-
DynamicJsonBuffer jsonBuffer;
96-
JsonObject& root = jsonBuffer.parseObject(response);
97-
if(root.success()) {
98-
if (root.containsKey("items")) {
99-
long subscriberCount = root["items"][0]["statistics"]["subscriberCount"];
100-
long viewCount = root["items"][0]["statistics"]["viewCount"];
101-
long commentCount = root["items"][0]["statistics"]["commentCount"];
102-
long hiddenSubscriberCount = root["items"][0]["statistics"]["hiddenSubscriberCount"];
103-
long videoCount = root["items"][0]["statistics"]["videoCount"];
104-
105-
channelStats.viewCount = viewCount;
106-
channelStats.subscriberCount = subscriberCount;
107-
channelStats.commentCount = commentCount;
108-
channelStats.hiddenSubscriberCount = hiddenSubscriberCount;
109-
channelStats.videoCount = videoCount;
110-
111-
return true;
112-
}
113-
}
11473

115-
return false;
74+
int strLen = channelId.length() + 1;
75+
char tempStr[strLen];
76+
channelId.toCharArray(tempStr, strLen);
77+
78+
return getChannelStatistics(tempStr);
79+
}
80+
81+
bool YoutubeApi::getChannelStatistics(char *channelId){
82+
char command[150] = YTAPI_CHANNEL_ENDPOINT;
83+
strcat(command, "?part=statistics&id=%s");
84+
sprintf(command, command, channelId);
85+
86+
if (_debug)
87+
{
88+
Serial.println(command);
89+
}
90+
91+
bool hasError = true;
92+
93+
// Get from https://arduinojson.org/v6/assistant/
94+
const size_t bufferSize = JSON_ARRAY_SIZE(1)
95+
+ JSON_OBJECT_SIZE(2)
96+
+ 2*JSON_OBJECT_SIZE(4)
97+
+ JSON_OBJECT_SIZE(5);
98+
99+
if (sendGetToYoutube(command) == 200)
100+
{
101+
// Allocate DynamicJsonDocument
102+
DynamicJsonDocument doc(bufferSize);
103+
104+
// Parse JSON object
105+
DeserializationError error = deserializeJson(doc, *client);
106+
if (!error)
107+
{
108+
hasError = false;
109+
110+
JsonObject itemStatistics = doc["items"][0]["statistics"];
111+
112+
channelStats.viewCount = itemStatistics["viewCount"].as<long>();
113+
channelStats.subscriberCount = itemStatistics["subscriberCount"].as<long>();
114+
channelStats.commentCount = itemStatistics["commentCount"].as<long>();
115+
channelStats.hiddenSubscriberCount = itemStatistics["hiddenSubscriberCount"].as<bool>();
116+
channelStats.videoCount = itemStatistics["videoCount"].as<long>();
117+
}
118+
else
119+
{
120+
Serial.print(F("deserializeJson() failed with code "));
121+
Serial.println(error.c_str());
122+
}
123+
}
124+
closeClient();
125+
126+
return hasError;
127+
}
128+
129+
void YoutubeApi::skipHeaders()
130+
{
131+
// Skip HTTP headers
132+
char endOfHeaders[] = "\r\n\r\n";
133+
if (!client->find(endOfHeaders))
134+
{
135+
Serial.println(F("Invalid response"));
136+
return;
137+
}
138+
139+
// Was getting stray characters between the headers and the body
140+
// This should toss them away
141+
while (client->available() && client->peek() != '{')
142+
{
143+
char c = 0;
144+
client->readBytes(&c, 1);
145+
if (_debug)
146+
{
147+
Serial.print("Tossing an unexpected character: ");
148+
Serial.println(c);
149+
}
150+
}
151+
}
152+
153+
int YoutubeApi::getHttpStatusCode()
154+
{
155+
// Check HTTP status
156+
if(client->find("HTTP/1.1")){
157+
int statusCode = client->parseInt();
158+
return statusCode;
159+
}
160+
161+
return -1;
116162
}
117163

118164
void YoutubeApi::closeClient() {

src/YoutubeApi.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ License along with this library; if not, write to the Free Software
1818
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1919
*/
2020

21+
#if defined(__GNUC__) || defined(__clang__)
22+
#define DEPRECATED __attribute__((deprecated))
23+
#elif defined(_MSC_VER)
24+
#define DEPRECATED __declspec(deprecated)
25+
#else
26+
#pragma message("WARNING: You need to implement DEPRECATED for this compiler")
27+
#define DEPRECATED
28+
#endif
29+
2130

2231
#ifndef YoutubeApi_h
2332
#define YoutubeApi_h
@@ -30,6 +39,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
3039
#define YTAPI_SSL_PORT 443
3140
#define YTAPI_TIMEOUT 1500
3241

42+
#define YTAPI_CHANNEL_ENDPOINT "/youtube/v3/channels"
3343

3444
struct channelStatistics{
3545
long viewCount;
@@ -42,17 +52,19 @@ struct channelStatistics{
4252
class YoutubeApi
4353
{
4454
public:
45-
YoutubeApi (String apiKey, Client &client);
46-
String sendGetToYoutube(String command);
47-
bool getChannelStatistics(String channelId);
55+
YoutubeApi (char *apiKey, Client &client);
56+
DEPRECATED YoutubeApi (String apiKey, Client &client);
57+
int sendGetToYoutube(char *command);
58+
bool getChannelStatistics(char *channelId);
59+
DEPRECATED bool getChannelStatistics(String channelId);
4860
channelStatistics channelStats;
4961
bool _debug = false;
5062

5163
private:
52-
String _apiKey;
64+
char *_apiKey;
5365
Client *client;
54-
const int maxMessageLength = 1000;
55-
bool checkForOkResponse(String response);
66+
int getHttpStatusCode();
67+
void skipHeaders();
5668
void closeClient();
5769
};
5870

0 commit comments

Comments
 (0)