File indexing completed on 2024-05-05 04:59:18
0001 /* 0002 This file is part of the KDE project 0003 Copyright (C) 2011 Ernesto Rodriguez Ortiz <eortiz@uci.cu> 0004 0005 This program is free software: you can redistribute it and/or modify 0006 it under the terms of the GNU General Public License as published by 0007 the Free Software Foundation, either version 3 of the License, or 0008 (at your option) any later version. 0009 0010 This program is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 GNU General Public License for more details. 0014 0015 You should have received a copy of the GNU General Public License 0016 along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 0018 */ 0019 0020 #include "mmsdownload.h" 0021 0022 const int SPEEDTIMER = 1000; // 1 second... 0023 0024 MmsDownload::MmsDownload(const QString &url, const QString &name, const QString &temp, int amountsThread) 0025 : QThread() 0026 , m_sourceUrl(url) 0027 , m_fileName(name) 0028 , m_fileTemp(temp) 0029 , m_amountThreads(amountsThread) 0030 , m_connectionsFails(0) 0031 , m_connectionsSuccessfully(0) 0032 , m_downloadedSize(0) 0033 , m_mms(NULL) 0034 { 0035 m_speedTimer = new QTimer(this); 0036 m_speedTimer->setInterval(SPEEDTIMER); 0037 connect(m_speedTimer, SIGNAL(timeout()), this, SLOT(slotSpeedChanged())); 0038 } 0039 0040 MmsDownload::~MmsDownload() 0041 { 0042 if (m_mms) { 0043 mmsx_close(m_mms); 0044 } 0045 m_speedTimer->stop(); 0046 m_speedTimer->deleteLater(); 0047 } 0048 0049 void MmsDownload::run() 0050 { 0051 if (isWorkingUrl()) { 0052 splitTransfer(); 0053 startTransfer(); 0054 } else { 0055 Q_EMIT signBrokenUrl(); 0056 quit(); 0057 } 0058 exec(); 0059 } 0060 0061 bool MmsDownload::isWorkingUrl() 0062 { 0063 /** Check if the URL is working, if it can't connect then not start the download.*/ 0064 m_mms = mmsx_connect(NULL, NULL, qstrdup(m_sourceUrl.toLatin1()), 1e9); 0065 return m_mms; 0066 } 0067 0068 void MmsDownload::splitTransfer() 0069 { 0070 /** We split the download in similar and each part is asigned to a thread and thi is saved in 0071 * a map named m_mapEndIni. If we resume the download, then the temporal file will exist 0072 * and we don't have to split the download only use it. 0073 */ 0074 m_amountThreads = mmsx_get_seekable(m_mms) ? m_amountThreads : 0; 0075 if (m_amountThreads == 0) { 0076 m_amountThreads = 1; 0077 Q_EMIT signNotAllowMultiDownload(); 0078 QFile::remove(m_fileTemp); 0079 } 0080 0081 const qulonglong total = mmsx_get_length(m_mms); 0082 Q_EMIT signTotalSize(total); 0083 0084 if (QFile::exists(m_fileTemp)) { 0085 unSerialization(); 0086 } else { 0087 int part = mmsx_get_length(m_mms) / m_amountThreads; 0088 int ini = 0; 0089 int end = 0; 0090 for (int i = 0; i < m_amountThreads; i++) { 0091 if (i + 1 == m_amountThreads) { 0092 part = total - ini; 0093 } 0094 end = ini + part; 0095 m_mapEndIni.insert(end, ini); 0096 ini += part; 0097 } 0098 } 0099 } 0100 0101 void MmsDownload::startTransfer() 0102 { 0103 m_speedTimer->start(); 0104 QMap<int, int>::const_iterator iterator = m_mapEndIni.constBegin(); 0105 while (iterator != m_mapEndIni.constEnd()) { 0106 auto *thread = new MmsThread(m_sourceUrl, m_fileName, iterator.value(), iterator.key()); 0107 m_threadList.append(thread); 0108 connect(thread, SIGNAL(finished()), this, SLOT(slotThreadFinish())); 0109 connect(thread, SIGNAL(signIsConnected(bool)), this, SLOT(slotIsThreadConnected(bool))); 0110 connect(thread, SIGNAL(signReading(int, int, int)), this, SLOT(slotRead(int, int, int))); 0111 thread->start(); 0112 ++iterator; 0113 } 0114 } 0115 0116 void MmsDownload::slotSpeedChanged() 0117 { 0118 /** Using the same speed calculating datasourcefactory uses (use all downloaded data 0119 * of the last 10 secs) 0120 */ 0121 qulonglong speed; 0122 if (m_prevDownloadedSizes.size()) { 0123 speed = (m_downloadedSize - m_prevDownloadedSizes.first()) / (SPEEDTIMER * m_prevDownloadedSizes.size() / 1000); // downloaded in 1 second 0124 } else { 0125 speed = 0; 0126 } 0127 0128 m_prevDownloadedSizes.append(m_downloadedSize); 0129 if (m_prevDownloadedSizes.size() > 10) 0130 m_prevDownloadedSizes.removeFirst(); 0131 0132 Q_EMIT signSpeed(speed); 0133 serialization(); 0134 } 0135 0136 void MmsDownload::stopTransfer() 0137 { 0138 /** Here only is called thread->stop() because when the thread finish it Q_EMIT a signal 0139 * and slotThreadFinish(); is called where the thread is delete calling deleteLater(); and 0140 * m_threadList is cleaning using removeAll(). 0141 */ 0142 foreach (MmsThread *thread, m_threadList) { 0143 thread->stop(); 0144 thread->quit(); 0145 } 0146 } 0147 0148 int MmsDownload::threadsAlive() 0149 { 0150 return m_threadList.size(); 0151 } 0152 0153 void MmsDownload::slotThreadFinish() 0154 { 0155 auto *thread = qobject_cast<MmsThread *>(QObject::sender()); 0156 m_threadList.removeAll(thread); 0157 thread->deleteLater(); 0158 0159 if (m_threadList.isEmpty()) { 0160 serialization(); 0161 quit(); 0162 } 0163 } 0164 0165 void MmsDownload::slotRead(int reading, int thread_end, int thread_in) 0166 { 0167 /** We update the status of the thread in the map and Q_EMIT a signal for update the download 0168 * speed. 0169 */ 0170 if (thread_in == thread_end) { 0171 m_mapEndIni.remove(thread_end); 0172 } else { 0173 m_mapEndIni[thread_end] = thread_in; 0174 } 0175 m_downloadedSize += reading; 0176 Q_EMIT signDownloaded(m_downloadedSize); 0177 } 0178 0179 void MmsDownload::slotIsThreadConnected(bool connected) 0180 { 0181 /** All threads Q_EMIT a signal connected with this slot, if they get connected successfully 0182 * the value of "connected" will be true, and will be false if they can't connected. When all 0183 * the threads emitted the signal the amount of m_connectionsSuccefusslly and m_connectionsFails 0184 * will be equal to m_amountThreads and we Q_EMIT a signal to restart the download in 0185 * mmstransfer using the amount of connections succefusslly connected. 0186 */ 0187 if (connected) { 0188 m_connectionsSuccessfully++; 0189 } else { 0190 m_connectionsFails++; 0191 } 0192 if ((m_connectionsFails != 0) && (m_connectionsFails + m_connectionsSuccessfully == m_amountThreads)) { 0193 Q_EMIT signRestartDownload(m_connectionsSuccessfully); 0194 } 0195 } 0196 0197 void MmsDownload::serialization() 0198 { 0199 /** Here we save the status of the download to the temporal file for resume the download 0200 * if we stop it. 0201 */ 0202 QFile file(m_fileTemp); 0203 file.open(QIODevice::WriteOnly); 0204 QDataStream out(&file); 0205 out << m_mapEndIni << m_downloadedSize << m_prevDownloadedSizes; 0206 file.close(); 0207 } 0208 0209 void MmsDownload::unSerialization() 0210 { 0211 /** Here we read the status of the download to the temporal file for resume the download 0212 */ 0213 QFile file(m_fileTemp); 0214 file.open(QIODevice::ReadOnly); 0215 QDataStream in(&file); 0216 in >> m_mapEndIni >> m_downloadedSize >> m_prevDownloadedSizes; 0217 file.close(); 0218 } 0219 0220 #include "moc_mmsdownload.cpp"