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"