Support new account page layout

This commit is contained in:
Sude 2015-05-12 02:19:16 +03:00
parent 21e6c6119d
commit b5cdf10078
4 changed files with 219 additions and 304 deletions

View File

@ -92,9 +92,9 @@ class Downloader
int HTTP_Login(const std::string& email, const std::string& password); int HTTP_Login(const std::string& email, const std::string& password);
std::vector<gameItem> getGames(); std::vector<gameItem> getGames();
std::vector<gameItem> getFreeGames(); std::vector<gameItem> getFreeGames();
std::vector<gameFile> getExtrasFromHTML(const std::string& html, const std::string& gamename, const std::string& gameid); std::vector<gameFile> getExtrasFromJSON(const Json::Value& json, const std::string& gamename);
std::string getGameDetailsHTML(const std::string& gamename, const std::string& gameid); Json::Value getGameDetailsJSON(const std::string& gameid);
std::string getSerialsFromHTML(const std::string& html); std::string getSerialsFromJSON(const Json::Value& json);
void saveSerials(const std::string& serials, const std::string& filepath); void saveSerials(const std::string& serials, const std::string& filepath);
static int progressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); static int progressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);

View File

@ -17,6 +17,7 @@
#include <sstream> #include <sstream>
#include <rhash.h> #include <rhash.h>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <json/json.h>
struct gameSpecificConfig struct gameSpecificConfig
{ {
@ -37,6 +38,8 @@ namespace Util
void filepathReplaceReservedStrings(std::string& str, const std::string& gamename, const unsigned int& platformId = 0, const std::string& dlcname = ""); void filepathReplaceReservedStrings(std::string& str, const std::string& gamename, const unsigned int& platformId = 0, const std::string& dlcname = "");
void setFilePermissions(const boost::filesystem::path& path, const boost::filesystem::perms& permissions); void setFilePermissions(const boost::filesystem::path& path, const boost::filesystem::perms& permissions);
int getTerminalWidth(); int getTerminalWidth();
void getDownloaderUrlsFromJSON(const Json::Value &root, std::vector<std::string> &urls);
std::vector<std::string> getDLCNamesFromJSON(const Json::Value &root);
} }
#endif // UTIL_H #endif // UTIL_H

View File

@ -261,38 +261,69 @@ int Downloader::getGameDetails()
if (!gogAPI->getError()) if (!gogAPI->getError())
{ {
game.filterWithPriorities(config); game.filterWithPriorities(config);
std::string gameDetailsHTML; Json::Value gameDetailsJSON;
if (game.extras.empty() && config.bExtras) // Try to get extras from account page if API didn't return any extras if (game.extras.empty() && config.bExtras) // Try to get extras from account page if API didn't return any extras
{ {
gameDetailsHTML = this->getGameDetailsHTML(gameItems[i].name, gameItems[i].id); gameDetailsJSON = this->getGameDetailsJSON(gameItems[i].id);
game.extras = this->getExtrasFromHTML(gameDetailsHTML, gameItems[i].name, gameItems[i].id); game.extras = this->getExtrasFromJSON(gameDetailsJSON, gameItems[i].name);
} }
if (config.bSaveSerials) if (config.bSaveSerials)
{ {
if (gameDetailsHTML.empty()) if (gameDetailsJSON.empty())
gameDetailsHTML = this->getGameDetailsHTML(gameItems[i].name, gameItems[i].id); gameDetailsJSON = this->getGameDetailsJSON(gameItems[i].id);
game.serials = this->getSerialsFromHTML(gameDetailsHTML); game.serials = this->getSerialsFromJSON(gameDetailsJSON);
} }
if (game.dlcs.empty() && bHasDLC && conf.bDLC) if (game.dlcs.empty() && bHasDLC && conf.bDLC)
{ {
if (gameDetailsJSON.empty())
gameDetailsJSON = this->getGameDetailsJSON(gameItems[i].id);
for (unsigned int j = 0; j < gameItems[i].dlcnames.size(); ++j) for (unsigned int j = 0; j < gameItems[i].dlcnames.size(); ++j)
{ {
gameDetails dlc; gameDetails dlc;
std::string gameDetailsHTML_dlc;
dlc = gogAPI->getGameDetails(gameItems[i].dlcnames[j], conf.iInstallerType, conf.iInstallerLanguage, config.bDuplicateHandler); dlc = gogAPI->getGameDetails(gameItems[i].dlcnames[j], conf.iInstallerType, conf.iInstallerLanguage, config.bDuplicateHandler);
dlc.filterWithPriorities(config);
if (dlc.extras.empty() && config.bExtras) // Try to get extras from account page if API didn't return any extras if (dlc.extras.empty() && config.bExtras) // Try to get extras from account page if API didn't return any extras
{ {
gameDetailsHTML_dlc = this->getGameDetailsHTML(gameItems[i].dlcnames[j], gameItems[i].id); // Make sure we get extras for the right DLC
dlc.extras = this->getExtrasFromHTML(gameDetailsHTML_dlc, gameItems[i].dlcnames[j], gameItems[i].id); for (unsigned int k = 0; k < gameDetailsJSON["dlcs"].size(); ++k)
{
std::vector<std::string> urls;
if (gameDetailsJSON["dlcs"][k].isMember("extras"))
Util::getDownloaderUrlsFromJSON(gameDetailsJSON["dlcs"][k]["extras"], urls);
if (!urls.empty())
{
if (urls[0].find("/" + gameItems[i].dlcnames[j] + "/") != std::string::npos)
{
dlc.extras = this->getExtrasFromJSON(gameDetailsJSON["dlcs"][k], gameItems[i].dlcnames[j]);
}
}
}
} }
if (config.bSaveSerials) if (config.bSaveSerials)
{ {
if (gameDetailsHTML_dlc.empty()) // Make sure we save serial for the right DLC
gameDetailsHTML_dlc = this->getGameDetailsHTML(gameItems[i].dlcnames[j], gameItems[i].id); for (unsigned int k = 0; k < gameDetailsJSON["dlcs"].size(); ++k)
dlc.serials = this->getSerialsFromHTML(gameDetailsHTML_dlc); {
std::vector<std::string> urls;
if (gameDetailsJSON["dlcs"][k].isMember("cdKey") && gameDetailsJSON["dlcs"][k].isMember("downloads"))
{
// Assuming that only DLC with installers can have serial
Util::getDownloaderUrlsFromJSON(gameDetailsJSON["dlcs"][k]["downloads"], urls);
}
if (!urls.empty())
{
if (urls[0].find("/" + gameItems[i].dlcnames[j] + "/") != std::string::npos)
{
dlc.serials = this->getSerialsFromJSON(gameDetailsJSON["dlcs"][k]);
}
}
}
} }
game.dlcs.push_back(dlc); game.dlcs.push_back(dlc);
} }
} }
@ -1970,77 +2001,38 @@ int Downloader::HTTP_Login(const std::string& email, const std::string& password
std::cout << curl_easy_strerror(result) << std::endl; std::cout << curl_easy_strerror(result) << std::endl;
} }
html = this->getResponse("https://www.gog.com/account/settings"); html = this->getResponse("https://www.gog.com/account/settings/personal");
std::string account_email, username; std::string email_lowercase = boost::algorithm::to_lower_copy(email); // boost::algorithm::to_lower does in-place modification but "email" is read-only so we need to make a copy of it
dom = parser.parseTree(html); dom = parser.parseTree(html);
it = dom.begin(); it = dom.begin();
end = dom.end(); end = dom.end();
bool bEmailFound = false;
bool bUsernameFound = false;
for (; it != end; ++it) for (; it != end; ++it)
{ {
if (it->tagName()=="input") if (it->tagName()=="strong")
{ {
it->parseAttributes(); it->parseAttributes();
if (it->attribute("id").second == "accountEditEmail") if (it->attribute("class").second == "settings-item__value settings-item__section")
{
account_email = it->attribute("value").second;
bEmailFound = true;
}
}
else if (it->tagName()=="span")
{
it->parseAttributes();
if (it->attribute("class").second == "nickname")
{ {
for (unsigned int i = 0; i < dom.number_of_children(it); ++i) for (unsigned int i = 0; i < dom.number_of_children(it); ++i)
{ {
tree<htmlcxx::HTML::Node>::iterator nick_it = dom.child(it, i); tree<htmlcxx::HTML::Node>::iterator tag_it = dom.child(it, i);
if (!nick_it->isTag() && !nick_it->isComment()) if (!tag_it->isTag() && !tag_it->isComment())
{ {
username = nick_it->text(); std::string tag_text = boost::algorithm::to_lower_copy(tag_it->text());
bUsernameFound = true; if (tag_text == email_lowercase)
{
res = 1; // Login successful
break;
}
} }
} }
} }
} }
if (res == 1) // Login was successful so no need to go through the remaining tags
if (bUsernameFound && bEmailFound)
break; break;
} }
// Convert to lowercase for comparison
std::string email_lowercase = boost::algorithm::to_lower_copy(email); // boost::algorithm::to_lower does in-place modification but "email" is read-only so we need to make a copy of it
boost::algorithm::to_lower(account_email);
boost::algorithm::to_lower(username);
if (email_lowercase == account_email || email_lowercase == username)
{
res = 1; // Login successful
}
else
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (Downloader::HTTP_Login)" << std::endl;
if (!bEmailFound || !bUsernameFound)
{
if (!bEmailFound)
std::cerr << "Could not find \"accountEditEmail\" input field on account settings page." << std::endl;
if (!bUsernameFound)
std::cerr << "Could not find username on account settings page." << std::endl;
}
else
{
if (email_lowercase != account_email)
std::cerr << "Email (" << email_lowercase << ") doesn't match account email (" << account_email << ")" << std::endl;
if (email_lowercase != username)
std::cerr << "Username (" << email_lowercase << ") doesn't match account username (" << username << ")" << std::endl;
}
#endif
res = 0; // Login failed
}
return res; return res;
} }
@ -2051,12 +2043,11 @@ std::vector<gameItem> Downloader::getGames()
Json::Value root; Json::Value root;
Json::Reader *jsonparser = new Json::Reader; Json::Reader *jsonparser = new Json::Reader;
int i = 1; int i = 1;
std::string html = ""; bool bAllPagesParsed = false;
std::string page_html = "";
do do
{ {
std::string response = this->getResponse("https://www.gog.com/account/ajax?a=gamesShelfMore&s=title&q=&t=0&p=" + std::to_string(i)); std::string response = this->getResponse("https://www.gog.com/account/getFilteredProducts?hasHiddenProducts=false&hiddenFlag=0&isUpdated=0&mediaType=1&sortBy=title&system=&page=" + std::to_string(i));
// Parse JSON // Parse JSON
if (!jsonparser->parse(response, root)) if (!jsonparser->parse(response, root))
@ -2071,163 +2062,61 @@ std::vector<gameItem> Downloader::getGames()
#ifdef DEBUG #ifdef DEBUG
std::cerr << "DEBUG INFO (Downloader::getGames)" << std::endl << root << std::endl; std::cerr << "DEBUG INFO (Downloader::getGames)" << std::endl << root << std::endl;
#endif #endif
page_html = root["html"].asString(); if (root["page"].asInt() == root["totalPages"].asInt())
html += page_html; bAllPagesParsed = true;
if (page_html.empty() && i == 1) if (root["products"].isArray())
{ {
std::cout << "No games were found on your account. Try --login to refresh your authorization." << std::endl; for (unsigned int i = 0; i < root["products"].size(); ++i)
}
i++;
} while (!page_html.empty());
delete jsonparser;
// Parse HTML to get game names
htmlcxx::HTML::ParserDom parser;
tree<htmlcxx::HTML::Node> dom = parser.parseTree(html);
tree<htmlcxx::HTML::Node>::iterator it = dom.begin();
tree<htmlcxx::HTML::Node>::iterator end = dom.end();
for (; it != end; ++it)
{
if (it->tagName()=="div")
{
it->parseAttributes();
std::string classname = it->attribute("class").second;
if (classname=="shelf_game")
{ {
Json::Value product = root["products"][i];
gameItem game; gameItem game;
// Game name is contained in data-gameindex attribute game.name = product["slug"].asString();
game.name = it->attribute("data-gameindex").second; game.id = product["id"].asString();
game.id = it->attribute("data-gameid").second;
// Get platform info unsigned int platform = 0;
std::string tags = it->attribute("data-title").second; if (product["worksOn"]["Windows"].asBool())
unsigned int platform = GlobalConstants::PLATFORM_WINDOWS; // The tags don't specify Windows support so assume that there's always a Windows version platform |= GlobalConstants::PLATFORM_WINDOWS;
if (product["worksOn"]["Mac"].asBool())
if (tags.find("linux") != std::string::npos)
platform |= GlobalConstants::PLATFORM_LINUX;
if (tags.find("osx mac") != std::string::npos)
platform |= GlobalConstants::PLATFORM_MAC; platform |= GlobalConstants::PLATFORM_MAC;
if (product["worksOn"]["Linux"].asBool())
platform |= GlobalConstants::PLATFORM_LINUX;
// Skip if platform doesn't match // Skip if platform doesn't match
if (config.bPlatformDetection && !(platform & config.iInstallerType)) if (config.bPlatformDetection && !(platform & config.iInstallerType))
continue; continue;
if (!game.name.empty() && !game.id.empty()) if (config.bDLC)
{ {
// Check for DLC int dlcCount = product["dlcCount"].asInt();
if (config.bDLC) if (dlcCount != 0)
{ {
tree<htmlcxx::HTML::Node>::iterator dlc_it = it; std::string gameinfo = this->getResponse("https://www.gog.com/account/gameDetails/" + game.id + ".json");
tree<htmlcxx::HTML::Node>::iterator dlc_end = it.end(); Json::Value info;
for (; dlc_it != dlc_end; ++dlc_it) if (!jsonparser->parse(gameinfo, info))
{ {
if (dlc_it->tagName()=="div") #ifdef DEBUG
{ std::cerr << "DEBUG INFO (Downloader::getGames)" << std::endl << gameinfo << std::endl;
dlc_it->parseAttributes(); #endif
std::string classname_dlc = dlc_it->attribute("class").second; std::cout << jsonparser->getFormatedErrorMessages();
if (classname_dlc == "shelf-game-dlc-counter") delete jsonparser;
{ exit(1);
std::string content; }
for (unsigned int i = 0; i < dom.number_of_children(dlc_it); ++i) else
{ {
tree<htmlcxx::HTML::Node>::iterator it = dom.child(dlc_it, i); #ifdef DEBUG
if (!it->isTag() && !it->isComment()) std::cerr << "DEBUG INFO (Downloader::getGames)" << std::endl << info << std::endl;
content += it->text(); #endif
} game.dlcnames = Util::getDLCNamesFromJSON(info["dlcs"]);
// Get game names if game has DLC
if (content.find("DLC")!=std::string::npos)
{
Json::Value root;
Json::Reader *jsonparser = new Json::Reader;
std::string gameDataUrl = "https://www.gog.com/account/ajax?a=gamesListDetails&g=" + game.id;
std::string json = this->getResponse(gameDataUrl);
// Parse JSON
if (!jsonparser->parse(json, root))
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (Downloader::getGames)" << std::endl << json << std::endl;
#endif
std::cout << jsonparser->getFormatedErrorMessages();
delete jsonparser;
exit(1);
}
#ifdef DEBUG
std::cerr << "DEBUG INFO (Downloader::getGames)" << std::endl << root << std::endl;
#endif
std::string html = root["details"]["html"].asString();
delete jsonparser;
// Parse HTML to get game names for DLC
htmlcxx::HTML::ParserDom parser;
tree<htmlcxx::HTML::Node> dom = parser.parseTree(html);
tree<htmlcxx::HTML::Node>::iterator it = dom.begin();
tree<htmlcxx::HTML::Node>::iterator end = dom.end();
for (; it != end; ++it)
{
if (it->tagName()=="div")
{
it->parseAttributes();
std::string gamename = it->attribute("data-gameindex").second;
if (!gamename.empty() && gamename!=game.name)
{
bool bDuplicate = false;
for (unsigned int i = 0; i < game.dlcnames.size(); ++i)
{
if (gamename == game.dlcnames[i])
{
bDuplicate = true;
break;
}
}
if (!bDuplicate)
game.dlcnames.push_back(gamename);
}
}
}
// Try getting game names for DLCs from extra links. Catches game names for DLCs that don't have installers.
it = dom.begin();
end = dom.end();
for (; it != end; ++it)
{
if (it->tagName()=="a")
{
it->parseAttributes();
std::string href = it->attribute("href").second;
std::string search_string = "/downlink/file/"; // Extra links: https://www.gog.com/downlink/file/gamename/id_number
if (href.find(search_string)!=std::string::npos)
{
std::string gamename;
gamename.assign(href.begin()+href.find(search_string)+search_string.length(), href.begin()+href.find_last_of("/"));
if (!gamename.empty() && gamename!=game.name)
{
bool bDuplicate = false;
for (unsigned int i = 0; i < game.dlcnames.size(); ++i)
{
if (gamename == game.dlcnames[i])
{
bDuplicate = true;
break;
}
}
if (!bDuplicate)
game.dlcnames.push_back(gamename);
}
}
}
}
}
}
}
} }
} }
games.push_back(game);
} }
games.push_back(game);
} }
} }
} i++;
} while (!bAllPagesParsed);
delete jsonparser;
return games; return games;
} }
@ -2267,9 +2156,9 @@ std::vector<gameItem> Downloader::getFreeGames()
return games; return games;
} }
std::string Downloader::getGameDetailsHTML(const std::string& gamename, const std::string& gameid) Json::Value Downloader::getGameDetailsJSON(const std::string& gameid)
{ {
std::string gameDataUrl = "https://www.gog.com/account/ajax?a=gamesListDetails&g=" + gameid; std::string gameDataUrl = "https://www.gog.com/account/gameDetails/" + gameid + ".json";
std::string json = this->getResponse(gameDataUrl); std::string json = this->getResponse(gameDataUrl);
// Parse JSON // Parse JSON
@ -2278,130 +2167,110 @@ std::string Downloader::getGameDetailsHTML(const std::string& gamename, const st
if (!jsonparser->parse(json, root)) if (!jsonparser->parse(json, root))
{ {
#ifdef DEBUG #ifdef DEBUG
std::cerr << "DEBUG INFO (Downloader::getGameDetailsHTML)" << std::endl << json << std::endl; std::cerr << "DEBUG INFO (Downloader::getGameDetailsJSON)" << std::endl << json << std::endl;
#endif #endif
std::cout << jsonparser->getFormatedErrorMessages(); std::cout << jsonparser->getFormatedErrorMessages();
delete jsonparser; delete jsonparser;
exit(1); exit(1);
} }
#ifdef DEBUG #ifdef DEBUG
std::cerr << "DEBUG INFO (Downloader::getGameDetailsHTML)" << std::endl << root << std::endl; std::cerr << "DEBUG INFO (Downloader::getGameDetailsJSON)" << std::endl << root << std::endl;
#endif #endif
std::string html = root["details"]["html"].asString();
delete jsonparser; delete jsonparser;
return html; return root;
} }
std::vector<gameFile> Downloader::getExtrasFromHTML(const std::string& html, const std::string& gamename, const std::string& gameid) std::vector<gameFile> Downloader::getExtrasFromJSON(const Json::Value& json, const std::string& gamename)
{ {
std::vector<gameFile> extras; std::vector<gameFile> extras;
htmlcxx::HTML::ParserDom parser; std::vector<std::string> downloaderUrls;
tree<htmlcxx::HTML::Node> dom = parser.parseTree(html); Util::getDownloaderUrlsFromJSON(json["extras"], downloaderUrls);
tree<htmlcxx::HTML::Node>::iterator it = dom.begin();
tree<htmlcxx::HTML::Node>::iterator end = dom.end(); for (unsigned int i = 0; i < json["extras"].size(); ++i)
for (; it != end; ++it)
{ {
if (it->tagName()=="a") std::string id, name, path, downloaderUrl;
name = json["extras"][i]["name"].asString();
downloaderUrl = json["extras"][i]["downloaderUrl"].asString();
id.assign(downloaderUrl.begin()+downloaderUrl.find_last_of("/")+1, downloaderUrl.end());
// Get path from download link
std::string url = gogAPI->getExtraLink(gamename, id);
url = htmlcxx::Uri::decode(url);
if (url.find("/extras/") != std::string::npos)
{ {
it->parseAttributes(); path.assign(url.begin()+url.find("/extras/"), url.begin()+url.find_first_of("?"));
std::string href = it->attribute("href").second; path = "/" + gamename + path;
// Extra links https://www.gog.com/downlink/file/gamename/id_number
if (href.find("/downlink/file/" + gamename + "/")!=std::string::npos)
{
std::string id, name, path;
id.assign(href.begin()+href.find_last_of("/")+1, href.end());
// Get path from download link
std::string url = gogAPI->getExtraLink(gamename, id);
url = htmlcxx::Uri::decode(url);
if (url.find("/extras/") != std::string::npos)
{
path.assign(url.begin()+url.find("/extras/"), url.begin()+url.find_first_of("?"));
path = "/" + gamename + path;
}
else
{
path.assign(url.begin()+url.find_last_of("/")+1, url.begin()+url.find_first_of("?"));
path = "/" + gamename + "/extras/" + path;
}
// Get name from path
name.assign(path.begin()+path.find_last_of("/")+1,path.end());
if (name.empty())
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (getExtrasFromHTML)" << std::endl;
std::cerr << "Skipped file without a name (game: " << gamename << ", gameid: " << gameid << ", fileid: " << id << ")" << std::endl;
#endif
continue;
}
extras.push_back(
gameFile ( false,
id,
name,
path,
std::string()
)
);
}
} }
else
{
path.assign(url.begin()+url.find_last_of("/")+1, url.begin()+url.find_first_of("?"));
path = "/" + gamename + "/extras/" + path;
}
// Get name from path if name was not specified
if (name.empty())
name.assign(path.begin()+path.find_last_of("/")+1,path.end());
if (name.empty())
{
#ifdef DEBUG
std::cerr << "DEBUG INFO (getExtrasFromJSON)" << std::endl;
std::cerr << "Skipped file without a name (game: " << gamename << ", fileid: " << id << ")" << std::endl;
#endif
continue;
}
extras.push_back(
gameFile ( false,
id,
name,
path,
std::string()
)
);
} }
return extras; return extras;
} }
std::string Downloader::getSerialsFromHTML(const std::string& html) std::string Downloader::getSerialsFromJSON(const Json::Value& json)
{ {
std::ostringstream serials; std::ostringstream serials;
htmlcxx::HTML::ParserDom parser; std::string cdkey = json["cdKey"].asString();
tree<htmlcxx::HTML::Node> dom = parser.parseTree(html); if (cdkey.find("<span>") == std::string::npos)
tree<htmlcxx::HTML::Node>::iterator it = dom.begin();
tree<htmlcxx::HTML::Node>::iterator end = dom.end();
for (; it != end; ++it)
{ {
if (it->tagName() == "div") serials << cdkey;
}
else
{
htmlcxx::HTML::ParserDom parser;
tree<htmlcxx::HTML::Node> dom = parser.parseTree(cdkey);
tree<htmlcxx::HTML::Node>::iterator it = dom.begin();
tree<htmlcxx::HTML::Node>::iterator end = dom.end();
for (; it != end; ++it)
{ {
it->parseAttributes(); std::string tag_text;
std::string classname = it->attribute("class").second; if (it->tagName() == "span")
if (classname == "list_serial_h")
{ {
for (unsigned int i = 0; i < dom.number_of_children(it); ++i) for (unsigned int j = 0; j < dom.number_of_children(it); ++j)
{ {
tree<htmlcxx::HTML::Node>::iterator serials_it = dom.child(it, i); tree<htmlcxx::HTML::Node>::iterator span_it = dom.child(it, j);
if (!serials_it->isComment()) if (!span_it->isTag() && !span_it->isComment())
{ tag_text = span_it->text();
std::string tag_text;
if (!serials_it->isTag())
{
if (!serials_it->text().empty())
tag_text = serials_it->text();
}
else if (serials_it->tagName() == "span")
{
for (unsigned int j = 0; j < dom.number_of_children(serials_it); ++j)
{
tree<htmlcxx::HTML::Node>::iterator serials_span_it = dom.child(serials_it, j);
if (!serials_span_it->isTag() && !serials_span_it->isComment())
tag_text = serials_span_it->text();
}
}
if (!tag_text.empty())
{
boost::regex expression("^\\h+|\\h+$");
std::string text = boost::regex_replace(tag_text, expression, "");
if (!text.empty())
serials << text << std::endl;
}
}
} }
} }
if (!tag_text.empty())
{
boost::regex expression("^\\h+|\\h+$");
std::string text = boost::regex_replace(tag_text, expression, "");
if (!text.empty())
serials << text << std::endl;
}
} }
} }

View File

@ -342,3 +342,46 @@ int Util::getTerminalWidth()
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
return static_cast<int>(w.ws_col); return static_cast<int>(w.ws_col);
} }
void Util::getDownloaderUrlsFromJSON(const Json::Value &root, std::vector<std::string> &urls)
{
if(root.size() > 0) {
for(Json::ValueIterator it = root.begin() ; it != root.end() ; ++it)
{
if (it.key() == "downloaderUrl")
urls.push_back(it->asString());
else
getDownloaderUrlsFromJSON(*it, urls);
}
}
return;
}
std::vector<std::string> Util::getDLCNamesFromJSON(const Json::Value &root)
{
std::vector<std::string> urls, dlcnames;
getDownloaderUrlsFromJSON(root, urls);
for (unsigned int i = 0; i < urls.size(); ++i)
{
std::string gamename;
std::string match_string = "gogdownloader://";
if (urls[i].find(match_string) == std::string::npos)
continue;
gamename.assign(urls[i].begin()+urls[i].find(match_string)+match_string.length(), urls[i].begin()+urls[i].find_last_of("/"));
bool bDuplicate = false;
for (unsigned int j = 0; j < dlcnames.size(); ++j)
{
if (gamename == dlcnames[j])
{
bDuplicate = true;
break;
}
}
if (!bDuplicate)
dlcnames.push_back(gamename);
}
return dlcnames;
}