Added version detection before resuming download

Tries to detect if remote file is different from local file before resuming
Remote XML data is saved when download is started
This commit is contained in:
Sude 2013-04-08 13:43:11 +03:00
parent 9d59b8f5cf
commit d4db79e783
2 changed files with 116 additions and 23 deletions

View File

@ -54,9 +54,9 @@ class Downloader
ProgressBar* progressbar; ProgressBar* progressbar;
protected: protected:
private: private:
CURLcode downloadFile(std::string url, std::string filepath); CURLcode downloadFile(const std::string& url, const std::string& filepath, const std::string& xml_data = std::string());
int repairFile(std::string url, std::string filepath, std::string xml_data = std::string(), std::string xml_dir = std::string()); int repairFile(const std::string& url, const std::string& filepath, const std::string& xml_data = std::string());
int downloadCovers(std::string gamename, std::string directory, std::string cover_xml_data); int downloadCovers(const std::string& gamename, const std::string& directory, const std::string& cover_xml_data);
int login(); int login();
int getGameDetails(); int getGameDetails();
void getGameList(); void getGameList();

View File

@ -353,7 +353,7 @@ void Downloader::repair()
continue; continue;
} }
std::cout << "Repairing file " << filepath << std::endl; std::cout << "Repairing file " << filepath << std::endl;
this->repairFile(url, filepath, XML, config.sXMLDirectory); this->repairFile(url, filepath, XML);
std::cout << std::endl; std::cout << std::endl;
} }
} }
@ -374,7 +374,7 @@ void Downloader::repair()
continue; continue;
} }
std::cout << "Repairing file " << filepath << std::endl; std::cout << "Repairing file " << filepath << std::endl;
this->repairFile(url, filepath, std::string(), config.sXMLDirectory); this->repairFile(url, filepath);
std::cout << std::endl; std::cout << std::endl;
} }
} }
@ -435,10 +435,13 @@ void Downloader::download()
// Download // Download
if (!url.empty()) if (!url.empty())
{ {
std::string XML;
if (!config.bNoRemoteXML)
XML = gogAPI->getXML(games[i].gamename, games[i].installers[j].id);
if (!games[i].installers[j].name.empty()) if (!games[i].installers[j].name.empty())
std::cout << "Dowloading: " << games[i].installers[j].name << std::endl; std::cout << "Dowloading: " << games[i].installers[j].name << std::endl;
std::cout << filepath << std::endl; std::cout << filepath << std::endl;
this->downloadFile(url, filepath); this->downloadFile(url, filepath, XML);
std::cout << std::endl; std::cout << std::endl;
} }
} }
@ -479,7 +482,7 @@ void Downloader::download()
} }
// Download a file, resume if possible // Download a file, resume if possible
CURLcode Downloader::downloadFile(std::string url, std::string filepath) CURLcode Downloader::downloadFile(const std::string& url, const std::string& filepath, const std::string& xml_data)
{ {
CURLcode res = CURLE_RECV_ERROR; // assume network error CURLcode res = CURLE_RECV_ERROR; // assume network error
bool bResume = false; bool bResume = false;
@ -489,6 +492,39 @@ CURLcode Downloader::downloadFile(std::string url, std::string filepath)
// Get directory from filepath // Get directory from filepath
boost::filesystem::path pathname = filepath; boost::filesystem::path pathname = filepath;
std::string directory = pathname.parent_path().string(); std::string directory = pathname.parent_path().string();
std::string filenameXML = pathname.filename().string() + ".xml";
// Using local XML data for version check before resuming
boost::filesystem::path local_xml_file;
if (config.sXMLDirectory.empty())
local_xml_file = config.sHome + "/.gogdownloader/xml/" + filenameXML;
else
local_xml_file = config.sXMLDirectory + "/" + filenameXML;
bool bSameVersion = true; // assume same version
bool bLocalXMLExists = boost::filesystem::exists(local_xml_file);
if (!xml_data.empty())
{
// Do version check if local XML file exists
if (bLocalXMLExists)
{
TiXmlDocument remote_xml, local_xml;
remote_xml.Parse(xml_data.c_str());
local_xml.LoadFile(local_xml_file.string());
TiXmlNode *fileNodeRemote = remote_xml.FirstChild("file");
TiXmlNode *fileNodeLocal = local_xml.FirstChild("file");
if (fileNodeRemote && fileNodeLocal)
{
TiXmlElement *fileElemRemote = fileNodeRemote->ToElement();
TiXmlElement *fileElemLocal = fileNodeLocal->ToElement();
std::string remoteHash = fileElemRemote->Attribute("md5");
std::string localHash = fileElemLocal->Attribute("md5");
if (remoteHash != localHash)
bSameVersion = false;
}
}
}
// Check that directory exists and create subdirectories // Check that directory exists and create subdirectories
boost::filesystem::path path = directory; boost::filesystem::path path = directory;
@ -512,19 +548,60 @@ CURLcode Downloader::downloadFile(std::string url, std::string filepath)
// Check if file exists // Check if file exists
if ((outfile=fopen(filepath.c_str(), "r"))!=NULL) if ((outfile=fopen(filepath.c_str(), "r"))!=NULL)
{ {
// File exists, resume if (bSameVersion)
if ((outfile = freopen(filepath.c_str(), "r+", outfile))!=NULL )
{ {
bResume = true; // File exists, resume
fseek(outfile, 0, SEEK_END); if ((outfile = freopen(filepath.c_str(), "r+", outfile))!=NULL )
offset = ftell(outfile); {
curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM, offset); bResume = true;
this->resume_position = offset; fseek(outfile, 0, SEEK_END);
offset = ftell(outfile);
curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM, offset);
this->resume_position = offset;
}
else
{
std::cout << "Failed to reopen " << filepath << std::endl;
return res;
}
} }
else else
{ {
std::cout << "Failed to reopen " << filepath << std::endl; fclose(outfile);
return res; std::cout << "Remote file is different, renaming local file" << std::endl;
boost::filesystem::path new_name = filepath + ".old";
if (boost::filesystem::exists(new_name))
{
std::cout << "Old renamed file found, deleting old file" << std::endl;
if (!boost::filesystem::remove(new_name))
{
std::cout << "Failed to delete " << new_name.string() << std::endl;
std::cout << "Skipping file" << std::endl;
return res;
}
}
boost::system::error_code ec;
boost::filesystem::rename(pathname, new_name, ec);
if (ec)
{
std::cout << "Failed to rename " << filepath << " to " << new_name.string() << std::endl;
std::cout << "Skipping file" << std::endl;
return res;
}
else
{
// Create new file
if ((outfile=fopen(filepath.c_str(), "w"))!=NULL)
{
curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM, 0); // start downloading from the beginning of file
this->resume_position = 0;
}
else
{
std::cout << "Failed to create " << filepath << std::endl;
return res;
}
}
} }
} }
else else
@ -532,7 +609,7 @@ CURLcode Downloader::downloadFile(std::string url, std::string filepath)
// File doesn't exist, create new file // File doesn't exist, create new file
if ((outfile=fopen(filepath.c_str(), "w"))!=NULL) if ((outfile=fopen(filepath.c_str(), "w"))!=NULL)
{ {
curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM, offset); // start downloading from the beginning of file curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM, 0); // start downloading from the beginning of file
this->resume_position = 0; this->resume_position = 0;
} }
else else
@ -542,6 +619,24 @@ CURLcode Downloader::downloadFile(std::string url, std::string filepath)
} }
} }
// Save remote XML
if (!xml_data.empty())
{
if ((bLocalXMLExists && (!bSameVersion || config.bRepair)) || !bLocalXMLExists)
{
std::ofstream ofs(local_xml_file.string().c_str());
if (ofs)
{
ofs << xml_data;
ofs.close();
}
else
{
std::cout << "Can't create " << local_xml_file.string() << std::endl;
}
}
}
curl_easy_setopt(curlhandle, CURLOPT_URL, url.c_str()); curl_easy_setopt(curlhandle, CURLOPT_URL, url.c_str());
curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, outfile); curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, outfile);
res = this->beginDownload(); res = this->beginDownload();
@ -561,13 +656,11 @@ CURLcode Downloader::downloadFile(std::string url, std::string filepath)
} }
// Repair file // Repair file
int Downloader::repairFile(std::string url, std::string filepath, std::string xml_data, std::string xml_dir) int Downloader::repairFile(const std::string& url, const std::string& filepath, const std::string& xml_data)
{ {
int res = 0; int res = 0;
FILE *outfile; FILE *outfile;
size_t offset=0; size_t offset=0;
if (xml_dir.empty())
xml_dir = config.sHome + "/.gogdownloader/xml";
size_t from_offset, to_offset; size_t from_offset, to_offset;
@ -592,7 +685,7 @@ int Downloader::repairFile(std::string url, std::string filepath, std::string xm
} }
else else
{ {
std::string xml_file = xml_dir + "/" + filename + ".xml"; std::string xml_file = config.sXMLDirectory + "/" + filename + ".xml";
std::cout << "XML: Using local file" << std::endl; std::cout << "XML: Using local file" << std::endl;
xml.LoadFile(xml_file); xml.LoadFile(xml_file);
} }
@ -669,7 +762,7 @@ int Downloader::repairFile(std::string url, std::string filepath, std::string xm
} }
else else
{ {
CURLcode result = this->downloadFile(url, filepath); CURLcode result = this->downloadFile(url, filepath, xml_data);
std::cout << std::endl; std::cout << std::endl;
if (result == CURLE_OK) if (result == CURLE_OK)
res = 1; res = 1;
@ -736,7 +829,7 @@ int Downloader::repairFile(std::string url, std::string filepath, std::string xm
} }
// Download cover images // Download cover images
int Downloader::downloadCovers(std::string gamename, std::string directory, std::string cover_xml_data) int Downloader::downloadCovers(const std::string& gamename, const std::string& directory, const std::string& cover_xml_data)
{ {
int res = 0; int res = 0;
TiXmlDocument xml; TiXmlDocument xml;