Just Intonation  Version 1.3.0 (18)
Explore scale-independent dynamically adapting tuning in just intonation
downloader.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2016-2017 Karolin Stange, Christoph Wick, and Haye Hinrichsen
3  *
4  * This file is part of JustIntonation.
5  *
6  * JustIntonation is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * JustIntonation is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with JustIntonation. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 //=============================================================================
21 // Downloader
22 //=============================================================================
23 
24 #include "downloader.h"
25 
26 #include <QNetworkInterface>
27 #include <QNetworkProxy>
28 #include <QFileInfo>
29 #include <QDir>
30 #include <QStandardPaths>
31 #include <QStorageInfo>
32 #include <QTimer>
33 
34 #include "config.h"
36 
37 //-----------------------------------------------------------------------------
38 // Downloader Constructor
39 //-----------------------------------------------------------------------------
40 
47 
48 Downloader::Downloader(QString url, QObject *parent)
49  : QObject(parent)
50  , mLocalPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))
51  , mRemotePath(url)
52  , mIndexFileName("index.txt")
53  , mNetworkManager()
54  , mIndexOfFiles()
55  , pNetworkReply(nullptr)
56  , mFile()
57  , mIsWaitingForConnection(false)
58  , mForcedDownload(false)
59  , mIsDownloading(false)
60 {
61  setModuleName("Downloader");
62 }
63 
64 
65 //-----------------------------------------------------------------------------
66 // Check internet connection
67 //-----------------------------------------------------------------------------
72 
74 {
75  QList<QNetworkInterface> networkInterfaces = QNetworkInterface::allInterfaces();
76  bool connectionFound = false;
77  LOGMESSAGE << "Checking network interfaces and internet connections:";
78  for (auto &networkInterface : networkInterfaces)
79  {
80  if ( networkInterface.flags().testFlag(QNetworkInterface::IsUp)
81  and not networkInterface.flags().testFlag(QNetworkInterface::IsLoopBack))
82  {
83  LOGMESSAGE << "Interface:" << networkInterface.name()
84  << "MAC:" << networkInterface.hardwareAddress() << ":";
85  if (networkInterface.addressEntries().empty())
86  { LOGMESSAGE << "This adapter is not connected."; }
87  else for (auto &entry : networkInterface.addressEntries())
88  {
89  LOGMESSAGE << "IP:" << entry.ip().toString()
90  << "/ Netmask:" << entry.netmask().toString();
91 
92  connectionFound = true;
93  }
94  }
95  }
96  return connectionFound;
97 }
98 
99 
100 //-----------------------------------------------------------------------------
101 // Start downloading
102 //-----------------------------------------------------------------------------
111 
112 void Downloader::start (bool forced)
113 {
114  LOGMESSAGE << "Start downloading, forced =" << forced;
115  if (mIsDownloading)
116  {
117  if (forced) stop();
118  else
119  {
120  LOGERROR << "Downloader is already downloading. Do not call start() twice.";
121  return;
122  }
123  }
124 
125  mForcedDownload = forced;
127  {
128  LOGMESSAGE << "Downloader is already waiting for connection. Ignore call to start().";
129  return;
130  }
131 
132  // Check for writeable app data storage location
133  QDir directory(mLocalPath);
134  if (not directory.exists()) directory.mkpath(".");
135  if (not directory.exists())
136  {
137  LOGERROR << "Cannot create local app data storage in" << mLocalPath;
138  return;
139  }
140 
141  if (not isConnectedToInternet())
142  {
143  LOGWARNING << "Not connected to the internet";
144  if (forced or getPathsOfDownloadedFiles().size()==0)
145  emit signalNoInternetConnection(forced);
146  }
147 
150 }
151 
152 
153 //-----------------------------------------------------------------------------
154 // Download index file
155 //-----------------------------------------------------------------------------
163 
165 {
166  LOGMESSAGE << "entering downloadIndexFile()";
167  if (not mIsWaitingForConnection) return;
168 
169  // Check internet connection
170  if (not isConnectedToInternet())
171  {
172  LOGWARNING << "Not connected to the internet, retry in 60 secs.";
173  QTimer::singleShot(60000,this,&Downloader::downloadIndexFile);
174  mIsWaitingForConnection = false;
175  return;
176  }
177  else mIsWaitingForConnection=false;
178 
179  // Detect proxy if necessary
180 #ifndef QT_NO_NETWORKPROXY
181  QNetworkProxyQuery query(QUrl(INT_SAMPLES_REPOSITORY));
182  QList<QNetworkProxy> listOfProxies = QNetworkProxyFactory::systemProxyForQuery(query);
183  LOGMESSAGE << "Number of available proxies" << listOfProxies.size();
184  LOGMESSAGE << "Proxy list:" << listOfProxies;
185  if (listOfProxies.size())
186  {
187  LOGMESSAGE << "Set the first proxy in the list";
188  QNetworkProxy::setApplicationProxy(listOfProxies[0]);
189  }
190 #endif
191 
192  // Start to download index file
193  connect(&mNetworkManager, &QNetworkAccessManager::finished,
195  QUrl index = QUrl(fullPath(mRemotePath,mIndexFileName));
196  pNetworkReply = mNetworkManager.get(QNetworkRequest(index));
197  if (pNetworkReply->error())
198  {
199  LOGWARNING << "Error requesting file" << index.fileName() << "for download";
200  LOGWARNING << "Message:" << pNetworkReply->errorString();
201  stop();
202  }
203  else
204  {
205  LOGMESSAGE << "Downloading file" << index;
206  mIsDownloading = true;
207  }
208  // Next step: indexFileDownloadFinished
209 }
210 
211 
212 //-----------------------------------------------------------------------------
213 // Stop downloading
214 //-----------------------------------------------------------------------------
219 
221 {
222  mIsWaitingForConnection = false;
223  if (mIsDownloading)
224  {
225  LOGMESSAGE << "Request to stop downloading";
226  if (pNetworkReply) if (pNetworkReply->isOpen()) pNetworkReply->abort();
227  if (mFile.isOpen()) mFile.close();
228  mIsDownloading = false;
229  }
230  return;
231 }
232 
233 
234 //-----------------------------------------------------------------------------
235 // Get a list of paths of all downloaded files
236 //-----------------------------------------------------------------------------
241 
243 {
244  QStringList nameFilter("*.int");
245  QDir directory(mLocalPath);
246  QStringList list = directory.entryList(nameFilter);
247  QFile indexFile(fullPath(mLocalPath,mIndexFileName));
248  QStringList filteredList;
249  if (not indexFile.open(QIODevice::ReadOnly)) filteredList = list;
250  else
251  {
252  QStringList indexFileContent;
253  QTextStream inputStream(&indexFile);
254  while (not inputStream.atEnd()) indexFileContent << inputStream.readLine();
255  if (indexFile.isOpen()) indexFile.close();
256  for (QString &e : indexFileContent) if (list.contains(e))
257  filteredList.append(fullPath(directory.path(),e));
258  }
259 
260  for (QString &e : filteredList) if (mIndexOfFiles.contains(e))
261  filteredList.append(fullPath(directory.path(),e));
262  return filteredList;
263 }
264 
265 
266 //-----------------------------------------------------------------------------
267 // Private slot: Index file downloaded
268 //-----------------------------------------------------------------------------
276 
277 void Downloader::indexFileDownloadFinished(QNetworkReply *pReply)
278 {
279  // Disconnect this slot from the network manager
280  disconnect(&mNetworkManager, &QNetworkAccessManager::finished,
282 
283  // Detect possible errors in the reply
284  if (pReply->error())
285  {
286  LOGWARNING << "A network error occured during download of the index file";
287  LOGWARNING << pReply->errorString();
288  stop();
289  return;
290  }
291 
292  // Read the index file into the QStringList mIndexOfFiles and delete reply
293  QTextStream inputStream(pReply);
294  mIndexOfFiles.clear();
295  while (not inputStream.atEnd()) mIndexOfFiles << inputStream.readLine();
296  pReply->deleteLater();
297  if (mIndexOfFiles.isEmpty())
298  {
299  LOGWARNING << mIndexFileName << "is empty or could not be downloaded.";
301  mIsDownloading=false;
302  return;
303  }
304 
305  // Write the index file to the local app storage
306  QFile indexFile(fullPath(mLocalPath,mIndexFileName));
307  if (not indexFile.open(QIODevice::WriteOnly))
308  {
309  LOGWARNING << "Could not open index file for writing: " << mIndexFileName;
310  mIsDownloading=false;
311  return;
312  }
313  QTextStream outputStream(&indexFile);
314  for (auto &line : mIndexOfFiles) outputStream << line << "\n";
315  if (indexFile.isOpen()) indexFile.close();
316  LOGMESSAGE << "Successfully downloaded" << pReply->url().toString();
317 
318  // Start downloading the listed files
320 }
321 
322 
323 //-----------------------------------------------------------------------------
324 // Private slot: Initiate download of next file
325 //-----------------------------------------------------------------------------
335 
337 {
338  if (mIndexOfFiles.isEmpty()) { mIsDownloading=false; return; }
339  QString versiontag = "." + QString::number(INT_FILEFORMAT_ROLLING_VERSION);
340  QString remotefile = fullPath(mRemotePath,mIndexOfFiles.first()+versiontag);
341  QNetworkRequest request;
342  request.setUrl(QUrl(remotefile));
343  connect(&mNetworkManager, &QNetworkAccessManager::finished,this,
345  pNetworkReply = mNetworkManager.head(request);
346 }
347 
348 
349 //-----------------------------------------------------------------------------
350 // Private slot: Start downloading if necessary
351 //-----------------------------------------------------------------------------
360 
362 {
363  // Disconnect this slot from the network manager
364  disconnect(&mNetworkManager, &QNetworkAccessManager::finished,this,
366 
367  // Determine remote file size and last-modified date
368  qint64 remotefilesize = pNetworkReply->header(QNetworkRequest::ContentLengthHeader).toUInt();
369  QDateTime remotefilelastmodified =
370  pNetworkReply->header(QNetworkRequest::LastModifiedHeader).value<QDateTime>();
371  pNetworkReply->deleteLater();
372  if (mIndexOfFiles.empty()) { mIsDownloading=false; return; }
373  QString filename = mIndexOfFiles.first();
374  QString versiontag = "." + QString::number(INT_FILEFORMAT_ROLLING_VERSION);
375  QString remotefile = fullPath(mRemotePath,filename+versiontag);
376  LOGMESSAGE << "Found file " << remotefile << ", size =" << remotefilesize;
377 
378  // Check whether there is already a local file of the same name
379  QString localfile = fullPath(mLocalPath,filename);
380  QFileInfo fileinfo(localfile);
381  if (fileinfo.exists() and fileinfo.isFile())
382  {
383  // If so, compare file size
384  qint64 localfilesize = fileinfo.size();
385  QDateTime localfilecreated = fileinfo.created();
386  LOGSTATUS << "Local file data:" << localfilecreated;
387  LOGSTATUS << "Remote file date:" << remotefilelastmodified;
388  if (localfilesize != remotefilesize or localfilecreated < remotefilelastmodified)
389  {
390  // If file are different delete local file
391  LOGWARNING << "Length of date is different, hence delete file" << localfile;
392  QFile::remove(localfile);
393  }
394  else if (mForcedDownload)
395  {
396  LOGMESSAGE << "Forced download: File" << localfile << "will be deleted";
397  QFile::remove(localfile);
398  }
399  else
400  {
401  // If files are identical
402  LOGMESSAGE << "File" << localfile << "already exists, skipping download.";
403  emit finalize();
404  return;
405  }
406  }
407 
408  // The download is started only if there is at least twice as much disk space
410  LOGSTATUS << "Determining free disk space at location" << mLocalPath;
411  LOGSTATUS << "Free disk space =" << diskspace;
412  if (2*remotefilesize > diskspace)
413  {
414  LOGWARNING << "Device does not provide enough disk space for downloading.";
415  mIsDownloading=false;
416  return;
417  }
418 
419  // Open temporary local file for writing
420  QString tmpfile = localfile + ".part";
421  mFile.setFileName(tmpfile);
422  if (not mFile.open(QIODevice::WriteOnly))
423  {
424  LOGWARNING << "Could not open file for writing: " << localfile;
425  mIsDownloading=false;
426  return;
427  }
428 
429  // Start downloading
430  LOGMESSAGE << "downloading" << remotefile << "to" << localfile;
431  connect(&mNetworkManager, &QNetworkAccessManager::finished,
433  pNetworkReply = mNetworkManager.get(QNetworkRequest(QUrl(remotefile)));
434  connect(pNetworkReply,&QNetworkReply::downloadProgress,
436  emit signalDownloading(true);
437 }
438 
439 
440 
441 
442 //-----------------------------------------------------------------------------
443 // Private slot: Download a data chunk and report progress
444 //-----------------------------------------------------------------------------
455 
456 void Downloader::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
457 {
458  if (pNetworkReply->isOpen())
459  {
460  LOGSTATUS << bytesReceived << "of" << bytesTotal << "bytes downloaded.";
461  emit signalProgress (mIndexOfFiles.size(),100.0*bytesReceived/bytesTotal);
462  QByteArray chunk = pNetworkReply->readAll();
463  mFile.write(chunk);
464  }
465 }
466 
467 
468 //-----------------------------------------------------------------------------
469 // Private slot: Download completed
470 //-----------------------------------------------------------------------------
479 
480 void Downloader::downloadComplete(QNetworkReply* pReply)
481 {
482  // Read the last chunk of data and cloe file
483  emit signalDownloading(false);
484  disconnect(&mNetworkManager, &QNetworkAccessManager::finished,
486  disconnect(pNetworkReply,&QNetworkReply::downloadProgress,
488  if (pReply->isOpen())
489  {
490  QByteArray lastchunk = pReply->readAll();
491  mFile.write(lastchunk);
492  if (mFile.isOpen()) mFile.close();
493 
494  // Rename partial temporary file to the final filename
495  if (mIndexOfFiles.isEmpty()) { mIsDownloading=false; return; }
496  QString filename = mIndexOfFiles.first();
497  QString localfile = fullPath(mLocalPath,filename);
498  QString tmpfile = localfile + ".part";
499  if (QFile::rename(tmpfile,localfile))
500  { LOGMESSAGE << "Completed download of file" << localfile; }
501  else LOGWARNING << "Could not rename temporary file" << tmpfile;
502  emit finalize();
503  emit signalNewFileDownloaded(filename);
504  }
505  else
506  {
507  LOGMESSAGE << "Download cancelled. Abort.";
508  QString filename = mIndexOfFiles.first();
509  QString localfile = fullPath(mLocalPath,filename);
510  QString tmpfile = localfile + ".part";
511  QFile::remove(tmpfile);
512  }
513  pReply->deleteLater();
514 }
515 
516 
517 
518 //-----------------------------------------------------------------------------
519 // Private slot: Finialize download
520 //-----------------------------------------------------------------------------
528 
530 {
531  // Finalize
532  mIndexOfFiles.removeFirst();
533  if (not mIndexOfFiles.isEmpty()) emit initiateDownloadOfNextFile();
534  else
535  {
536  LOGMESSAGE << "Downloader shutting down.";
538  mIsDownloading = false;
539  }
540 }
541 
542 
543 //-----------------------------------------------------------------------------
544 // Helper function: Construct a full path out of a path and a file name
545 //-----------------------------------------------------------------------------
552 
553 QString Downloader::fullPath(const QString &path, const QString &file) const
554 {
555  if (not path.endsWith('/')) return path+"/"+file;
556  else return path+file;
557 }
558 
559 
560 
void signalNewFileDownloaded(QString localpath)
void checkWhetherFileExistsAndDownload()
Private slot: Start downloading if necessary.
Definition: downloader.cpp:361
QString mLocalPath
Local path of the file to be downloaded.
Definition: downloader.h:83
void downloadIndexFile()
Download index file.
Definition: downloader.cpp:164
void setModuleName(const QString &name)
Specify the name of the class-specific module.
Definition: log.cpp:82
void signalDownloading(QVariant downloading)
void stop()
Stop downloading.
Definition: downloader.cpp:220
void finalize()
Finalize download.
Definition: downloader.cpp:529
bool isConnectedToInternet()
Check internet connection.
Definition: downloader.cpp:73
bool mForcedDownload
Flag for forced download.
Definition: downloader.h:91
static PlatformTools & getSingleton()
Definition: platformtools.h:56
#define LOGMESSAGE
Definition: log.h:43
#define INT_SAMPLES_REPOSITORY
Definition: config.h:44
Downloader(QString url, QObject *parent=0)
Constructor, resetting the member variables.
Definition: downloader.cpp:48
void start(bool forced)
Start the downloading process in the background.
Definition: downloader.cpp:112
void signalNoInternetConnection(QVariant forced)
QNetworkAccessManager mNetworkManager
Instance of the Qt network access manager.
Definition: downloader.h:86
QString mRemotePath
Remote path of the file to be downloaded.
Definition: downloader.h:84
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
Private slot: Download a data chunk and report progress.
Definition: downloader.cpp:456
QNetworkReply * pNetworkReply
Pointer to the network reply structure.
Definition: downloader.h:88
void initiateDownloadOfNextFile()
Private slot: Initiate download of next file.
Definition: downloader.cpp:336
#define LOGERROR
Definition: log.h:45
QString fullPath(const QString &path, const QString &file) const
Helper function: Construct a full path out of a path and a file name.
Definition: downloader.cpp:553
virtual qint64 getFreeDiskSpace(const QString &path)
Determine free disk space.
Definition: platformtools.h:93
QString mIndexFileName
Name of the remote index file.
Definition: downloader.h:85
#define INT_FILEFORMAT_ROLLING_VERSION
Definition: config.h:40
QFile mFile
File to be written.
Definition: downloader.h:89
#define LOGSTATUS
Definition: log.h:42
bool mIsWaitingForConnection
Flag indicating waiting status.
Definition: downloader.h:90
void signalProgress(QVariant filesRemaining, QVariant percent)
void signalAllFilesDownloaded()
QStringList getPathsOfDownloadedFiles()
Get a list of paths of all downloaded files.
Definition: downloader.cpp:242
void indexFileDownloadFinished(QNetworkReply *pReply)
Private slot: Index file downloaded.
Definition: downloader.cpp:277
QStringList mIndexOfFiles
The content of the index file as a QStringList.
Definition: downloader.h:87
void downloadComplete(QNetworkReply *pReply)
Private slot: Download completed.
Definition: downloader.cpp:480
bool mIsDownloading
Flag indicating active download.
Definition: downloader.h:92
#define LOGWARNING
Definition: log.h:44