1 /*
2 This file is part of the KDE project
3 Copyright (C) 2011 Ernesto Rodriguez Ortiz <eortiz@uci.cu>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 */
19
20 #include "mmsdownload.h"
21
22 const int SPEEDTIMER = 1000;//1 second...
23
MmsDownload(const QString & url,const QString & name,const QString & temp,int amountsThread)24 MmsDownload::MmsDownload(const QString &url, const QString &name, const QString &temp,
25 int amountsThread)
26 : QThread(),
27 m_sourceUrl(url),
28 m_fileName(name),
29 m_fileTemp(temp),
30 m_amountThreads(amountsThread),
31 m_connectionsFails(0),
32 m_connectionsSuccessfully(0),
33 m_downloadedSize(0),
34 m_mms(NULL)
35 {
36 m_speedTimer = new QTimer(this);
37 m_speedTimer->setInterval(SPEEDTIMER);
38 connect(m_speedTimer, SIGNAL(timeout()), this, SLOT(slotSpeedChanged()));
39 }
40
~MmsDownload()41 MmsDownload::~MmsDownload()
42 {
43 if (m_mms) {
44 mmsx_close(m_mms);
45 }
46 m_speedTimer->stop();
47 m_speedTimer->deleteLater();
48 }
49
run()50 void MmsDownload::run()
51 {
52 if (isWorkingUrl()) {
53 splitTransfer();
54 startTransfer();
55 } else {
56 Q_EMIT signBrokenUrl();
57 quit();
58 }
59 exec();
60 }
61
62
isWorkingUrl()63 bool MmsDownload::isWorkingUrl()
64 {
65 /** Check if the URL is working, if it can't connect then not start the download.*/
66 m_mms = mmsx_connect(NULL, NULL, qstrdup(m_sourceUrl.toLatin1()), 1e9);
67 return m_mms;
68 }
69
splitTransfer()70 void MmsDownload::splitTransfer()
71 {
72 /** We split the download in similar and each part is asigned to a thread and thi is saved in
73 * a map named m_mapEndIni. If we resume the download, then the temporal file will exist
74 * and we don't have to split the download only use it.
75 */
76 m_amountThreads = mmsx_get_seekable(m_mms) ? m_amountThreads : 0;
77 if (m_amountThreads == 0) {
78 m_amountThreads = 1;
79 Q_EMIT signNotAllowMultiDownload();
80 QFile::remove(m_fileTemp);
81 }
82
83 const qulonglong total = mmsx_get_length(m_mms);
84 Q_EMIT signTotalSize(total);
85
86 if (QFile::exists(m_fileTemp)) {
87 unSerialization();
88 } else {
89 int part = mmsx_get_length(m_mms) / m_amountThreads;
90 int ini = 0;
91 int end = 0;
92 for (int i = 0; i < m_amountThreads; i++) {
93 if (i + 1 == m_amountThreads) {
94 part = total - ini;
95 }
96 end = ini + part;
97 m_mapEndIni.insert(end, ini);
98 ini += part;
99 }
100 }
101 }
102
startTransfer()103 void MmsDownload::startTransfer()
104 {
105 m_speedTimer->start();
106 QMap<int, int>::const_iterator iterator = m_mapEndIni.constBegin();
107 while (iterator != m_mapEndIni.constEnd()) {
108 auto* thread = new MmsThread(m_sourceUrl, m_fileName,
109 iterator.value(), iterator.key());
110 m_threadList.append(thread);
111 connect(thread, SIGNAL(finished()), this, SLOT(slotThreadFinish()));
112 connect(thread, SIGNAL(signIsConnected(bool)), this, SLOT(slotIsThreadConnected(bool)));
113 connect(thread, SIGNAL(signReading(int,int,int)), this, SLOT(slotRead(int,int,int)));
114 thread->start();
115 ++iterator;
116 }
117 }
118
slotSpeedChanged()119 void MmsDownload::slotSpeedChanged()
120 {
121 /** Using the same speed calculating datasourcefactory uses (use all downloaded data
122 * of the last 10 secs)
123 */
124 qulonglong speed;
125 if (m_prevDownloadedSizes.size()) {
126 speed = (m_downloadedSize - m_prevDownloadedSizes.first()) / (SPEEDTIMER *
127 m_prevDownloadedSizes.size() / 1000);//downloaded in 1 second
128 } else {
129 speed = 0;
130 }
131
132 m_prevDownloadedSizes.append(m_downloadedSize);
133 if(m_prevDownloadedSizes.size() > 10)
134 m_prevDownloadedSizes.removeFirst();
135
136 Q_EMIT signSpeed(speed);
137 serialization();
138 }
139
140
stopTransfer()141 void MmsDownload::stopTransfer()
142 {
143 /** Here only is called thread->stop() because when the thread finish it Q_EMIT a signal
144 * and slotThreadFinish(); is called where the thread is delete calling deleteLater(); and
145 * m_threadList is cleaning using removeAll().
146 */
147 foreach (MmsThread* thread, m_threadList) {
148 thread->stop();
149 thread->quit();
150 }
151 }
152
threadsAlive()153 int MmsDownload::threadsAlive()
154 {
155 return m_threadList.size();
156 }
157
158
slotThreadFinish()159 void MmsDownload::slotThreadFinish()
160 {
161 auto* thread = qobject_cast<MmsThread*>(QObject::sender());
162 m_threadList.removeAll(thread);
163 thread->deleteLater();
164
165 if (m_threadList.isEmpty()) {
166 serialization();
167 quit();
168 }
169 }
170
slotRead(int reading,int thread_end,int thread_in)171 void MmsDownload::slotRead(int reading, int thread_end, int thread_in)
172 {
173 /** We update the status of the thread in the map and Q_EMIT a signal for update the download
174 * speed.
175 */
176 if (thread_in == thread_end) {
177 m_mapEndIni.remove(thread_end);
178 } else {
179 m_mapEndIni[thread_end] = thread_in;
180 }
181 m_downloadedSize += reading;
182 Q_EMIT signDownloaded(m_downloadedSize);
183 }
184
slotIsThreadConnected(bool connected)185 void MmsDownload::slotIsThreadConnected(bool connected)
186 {
187 /** All threads Q_EMIT a signal connected with this slot, if they get connected successfully
188 * the value of "connected" will be true, and will be false if they can't connected. When all
189 * the threads emitted the signal the amount of m_connectionsSuccefusslly and m_connectionsFails
190 * will be equal to m_amountThreads and we Q_EMIT a signal to restart the download in
191 * mmstransfer using the amount of connections succefusslly connected.
192 */
193 if (connected) {
194 m_connectionsSuccessfully++;
195 } else {
196 m_connectionsFails++;
197 }
198 if ((m_connectionsFails != 0) &&
199 (m_connectionsFails + m_connectionsSuccessfully == m_amountThreads)) {
200 Q_EMIT signRestartDownload(m_connectionsSuccessfully);
201 }
202 }
203
serialization()204 void MmsDownload::serialization()
205 {
206 /** Here we save the status of the download to the temporal file for resume the download
207 * if we stop it.
208 */
209 QFile file(m_fileTemp);
210 file.open(QIODevice::WriteOnly);
211 QDataStream out(&file);
212 out << m_mapEndIni << m_downloadedSize << m_prevDownloadedSizes;
213 file.close();
214 }
215
unSerialization()216 void MmsDownload::unSerialization()
217 {
218 /** Here we read the status of the download to the temporal file for resume the download
219 */
220 QFile file(m_fileTemp);
221 file.open(QIODevice::ReadOnly);
222 QDataStream in(&file);
223 in >> m_mapEndIni >> m_downloadedSize >> m_prevDownloadedSizes;
224 file.close();
225 }
226