Skip to content

Source updates + string bugfix #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 6, 2021
216 changes: 104 additions & 112 deletions src/YoutubeApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,156 +27,148 @@

#include "YoutubeApi.h"

YoutubeApi::YoutubeApi(String apiKey, Client &client) {
int strLen = apiKey.length() + 1;
char tempStr[strLen];
apiKey.toCharArray(tempStr, strLen);

YoutubeApi(tempStr, client);
}

YoutubeApi::YoutubeApi(char *apiKey, Client &client) {
_apiKey = apiKey;
this->client = &client;
}

int YoutubeApi::sendGetToYoutube(char *command) {
client->flush();
client->setTimeout(YTAPI_TIMEOUT);
if (!client->connect(YTAPI_HOST, YTAPI_SSL_PORT))
{
Serial.println(F("Connection failed"));
return false;
}
YoutubeApi::YoutubeApi(const char* key, Client &client)
: apiKey(key), client(client)
{}

YoutubeApi::YoutubeApi(const String &apiKey, Client &client)
: YoutubeApi(apiKey.c_str(), client) // passing the key as c-string to force a copy
{}

int YoutubeApi::sendGetToYoutube(const char *command) {
client.flush();
client.setTimeout(YTAPI_TIMEOUT);
if (!client.connect(YTAPI_HOST, YTAPI_SSL_PORT))
{
Serial.println(F("Connection failed"));
return false;
}
// give the esp a breather
yield();
yield();

// Send HTTP request
client->print(F("GET "));
client->print(command);
client->println(F(" HTTP/1.1"));
// Send HTTP request
client.print(F("GET "));
client.print(command);
client.println(F(" HTTP/1.1"));

//Headers
client->print(F("Host: "));
client->println(YTAPI_HOST);
client.print(F("Host: "));
client.println(YTAPI_HOST);

client->println(F("Cache-Control: no-cache"));
client.println(F("Cache-Control: no-cache"));

if (client->println() == 0)
{
Serial.println(F("Failed to send request"));
return -1;
}
if (client.println() == 0)
{
Serial.println(F("Failed to send request"));
return -1;
}

int statusCode = getHttpStatusCode();

// Let the caller of this method parse the JSon from the client
skipHeaders();
return statusCode;
}

bool YoutubeApi::getChannelStatistics(String channelId){

int strLen = channelId.length() + 1;
char tempStr[strLen];
channelId.toCharArray(tempStr, strLen);
// Let the caller of this method parse the JSon from the client
skipHeaders();
return statusCode;
}

return getChannelStatistics(tempStr);
int YoutubeApi::sendGetToYoutube(const String& command) {
return sendGetToYoutube(command.c_str());
}

bool YoutubeApi::getChannelStatistics(char *channelId){
bool YoutubeApi::getChannelStatistics(const char *channelId) {
char command[150] = YTAPI_CHANNEL_ENDPOINT;
char params[120];
sprintf(params, "?part=statistics&id=%s&key=%s", channelId, _apiKey);
strcat(command, params);
char params[120];
sprintf(params, "?part=statistics&id=%s&key=%s", channelId, apiKey.c_str());
strcat(command, params);

if (_debug)
{
Serial.println(command);
}
if (_debug)
{
Serial.println(command);
}

bool wasSuccessful = false;

// Get from https://arduinojson.org/v6/assistant/
const size_t bufferSize = JSON_ARRAY_SIZE(1)
+ JSON_OBJECT_SIZE(2)
+ 2*JSON_OBJECT_SIZE(4)
+ JSON_OBJECT_SIZE(5)
+ 330;
// Get from https://arduinojson.org/v6/assistant/
const size_t bufferSize = JSON_ARRAY_SIZE(1)
+ JSON_OBJECT_SIZE(2)
+ 2*JSON_OBJECT_SIZE(4)
+ JSON_OBJECT_SIZE(5)
+ 330;

int httpStatus = sendGetToYoutube(command);
int httpStatus = sendGetToYoutube(command);

if (httpStatus == 200)
{
// Allocate DynamicJsonDocument
DynamicJsonDocument doc(bufferSize);
if (httpStatus == 200)
{
// Allocate DynamicJsonDocument
DynamicJsonDocument doc(bufferSize);

// Parse JSON object
DeserializationError error = deserializeJson(doc, *client);
if (!error)
{
// Parse JSON object
DeserializationError error = deserializeJson(doc, client);
if (!error)
{
wasSuccessful = true;

JsonObject itemStatistics = doc["items"][0]["statistics"];
JsonObject itemStatistics = doc["items"][0]["statistics"];

channelStats.viewCount = itemStatistics["viewCount"].as<long>();
channelStats.subscriberCount = itemStatistics["subscriberCount"].as<long>();
channelStats.commentCount = itemStatistics["commentCount"].as<long>();
channelStats.hiddenSubscriberCount = itemStatistics["hiddenSubscriberCount"].as<bool>();
channelStats.videoCount = itemStatistics["videoCount"].as<long>();
}
else
{
Serial.print(F("deserializeJson() failed with code "));
Serial.println(error.c_str());
}
} else {
Serial.print("Unexpected HTTP Status Code: ");
Serial.println(httpStatus);
}
closeClient();
}
else
{
Serial.print(F("deserializeJson() failed with code "));
Serial.println(error.c_str());
}
} else {
Serial.print("Unexpected HTTP Status Code: ");
Serial.println(httpStatus);
}
closeClient();

return wasSuccessful;
}

void YoutubeApi::skipHeaders()
{
// Skip HTTP headers
char endOfHeaders[] = "\r\n\r\n";
if (!client->find(endOfHeaders))
{
Serial.println(F("Invalid response"));
return;
}

// Was getting stray characters between the headers and the body
// This should toss them away
while (client->available() && client->peek() != '{')
{
char c = 0;
client->readBytes(&c, 1);
if (_debug)
{
Serial.print("Tossing an unexpected character: ");
Serial.println(c);
}
}
bool YoutubeApi::getChannelStatistics(const String& channelId) {
return getChannelStatistics(channelId.c_str());
}

void YoutubeApi::skipHeaders() {
// Skip HTTP headers
char endOfHeaders[] = "\r\n\r\n";
if (!client.find(endOfHeaders))
{
Serial.println(F("Invalid response"));
return;
}

// Was getting stray characters between the headers and the body
// This should toss them away
while (client.available() && client.peek() != '{')
{
char c = 0;
client.readBytes(&c, 1);
if (_debug)
{
Serial.print("Tossing an unexpected character: ");
Serial.println(c);
}
}
}

int YoutubeApi::getHttpStatusCode()
{
// Check HTTP status
if(client->find("HTTP/1.1")){
int statusCode = client->parseInt();
return statusCode;
}
int YoutubeApi::getHttpStatusCode() {
// Check HTTP status
if(client.find("HTTP/1.1")){
int statusCode = client.parseInt();
return statusCode;
}

return -1;
return -1;
}

void YoutubeApi::closeClient() {
if(client->connected()) {
if(client.connected()) {
if(_debug) { Serial.println(F("Closing client")); }
client->stop();
client.stop();
}
}
41 changes: 21 additions & 20 deletions src/YoutubeApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,32 @@

#define YTAPI_CHANNEL_ENDPOINT "/youtube/v3/channels"

struct channelStatistics{
long viewCount;
long commentCount; /* DEPRECATED */
long subscriberCount;
bool hiddenSubscriberCount;
long videoCount;
struct channelStatistics {
long viewCount;
long commentCount; /* DEPRECATED */
long subscriberCount;
bool hiddenSubscriberCount;
long videoCount;
};

class YoutubeApi
{
public:
YoutubeApi (char *apiKey, Client &client);
YoutubeApi (String apiKey, Client &client);
int sendGetToYoutube(char *command);
bool getChannelStatistics(char *channelId);
bool getChannelStatistics(String channelId);
channelStatistics channelStats;
bool _debug = false;
public:
YoutubeApi(const char *key, Client &client);
YoutubeApi(const String& apiKey, Client& client);
int sendGetToYoutube(const char *command);
int sendGetToYoutube(const String& command);
bool getChannelStatistics(const char *channelId);
bool getChannelStatistics(const String& channelId);
channelStatistics channelStats;
bool _debug = false;

private:
char *_apiKey;
Client *client;
int getHttpStatusCode();
void skipHeaders();
void closeClient();
private:
const String apiKey;
Client &client;
int getHttpStatusCode();
void skipHeaders();
void closeClient();
};

#endif