File indexing completed on 2024-05-19 05:00:08
0001 /* This file is part of the KDE project 0002 0003 Copyright (C) 2008 Manolo Valdes <nolis71cu@gmail.com> 0004 Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 */ 0011 0012 #include "multisegkiodatasource.h" 0013 #include "core/transfer.h" 0014 #include "segment.h" 0015 0016 #include "kget_debug.h" 0017 #include <QDebug> 0018 0019 MultiSegKioDataSource::MultiSegKioDataSource(const QUrl &srcUrl, QObject *parent) 0020 : TransferDataSource(srcUrl, parent) 0021 , m_size(0) 0022 , m_canResume(false) 0023 , m_started(false) 0024 { 0025 qCDebug(KGET_DEBUG) << "Create MultiSegKioDataSource for" << m_sourceUrl << this; 0026 setCapabilities(capabilities() | Transfer::Cap_FindFilesize); 0027 } 0028 0029 MultiSegKioDataSource::~MultiSegKioDataSource() 0030 { 0031 qCDebug(KGET_DEBUG) << this; 0032 } 0033 0034 void MultiSegKioDataSource::start() 0035 { 0036 qCDebug(KGET_DEBUG) << this; 0037 0038 m_started = true; 0039 foreach (Segment *segment, m_segments) { 0040 segment->startTransfer(); 0041 } 0042 } 0043 0044 void MultiSegKioDataSource::stop() 0045 { 0046 qCDebug(KGET_DEBUG) << this << m_segments.count() << "segments stopped."; 0047 0048 m_started = false; 0049 foreach (Segment *segment, m_segments) { 0050 if (segment->findingFileSize()) { 0051 qCDebug(KGET_DEBUG) << "Removing findingFileSize segment" << this; 0052 m_segments.removeAll(segment); 0053 segment->deleteLater(); 0054 } else { 0055 segment->stopTransfer(); 0056 } 0057 } 0058 } 0059 0060 QList<QPair<int, int>> MultiSegKioDataSource::assignedSegments() const 0061 { 0062 QList<QPair<int, int>> assigned; 0063 foreach (Segment *segment, m_segments) { 0064 assigned.append(segment->assignedSegments()); 0065 } 0066 0067 return assigned; 0068 } 0069 0070 void MultiSegKioDataSource::addSegments(const QPair<KIO::fileoffset_t, KIO::fileoffset_t> &segmentSize, const QPair<int, int> &segmentRange) 0071 { 0072 auto *segment = new Segment(m_sourceUrl, segmentSize, segmentRange, this); 0073 m_segments.append(segment); 0074 0075 connect(segment, &Segment::canResume, this, &MultiSegKioDataSource::slotCanResume); 0076 connect(segment, &Segment::totalSize, this, &MultiSegKioDataSource::slotTotalSize); 0077 connect(segment, &Segment::data, this, [this](KIO::fileoffset_t offset, const QByteArray &data, bool &worked) { 0078 this->data(offset, data, worked); 0079 }); 0080 connect(segment, &Segment::finishedSegment, this, &MultiSegKioDataSource::slotFinishedSegment); 0081 connect(segment, &Segment::error, this, &MultiSegKioDataSource::slotError); 0082 connect(segment, &Segment::finishedDownload, this, &MultiSegKioDataSource::slotFinishedDownload); 0083 connect(segment, &Segment::urlChanged, this, &MultiSegKioDataSource::slotUrlChanged); 0084 0085 if (m_started) { 0086 segment->startTransfer(); 0087 } 0088 } 0089 0090 void MultiSegKioDataSource::slotUrlChanged(const QUrl &url) 0091 { 0092 if (m_sourceUrl != url) { 0093 Q_EMIT urlChanged(m_sourceUrl, url); 0094 m_sourceUrl = url; 0095 } 0096 } 0097 0098 void MultiSegKioDataSource::findFileSize(KIO::fileoffset_t segmentSize) 0099 { 0100 addSegments(qMakePair(segmentSize, segmentSize), qMakePair(-1, -1)); 0101 Segment *segment = m_segments.last(); 0102 segment->startTransfer(); 0103 } 0104 0105 void MultiSegKioDataSource::slotSpeed(ulong downloadSpeed) 0106 { 0107 m_speed = downloadSpeed; 0108 Q_EMIT speed(m_speed); 0109 } 0110 0111 void MultiSegKioDataSource::slotFinishedSegment(Segment *segment, int segmentNum, bool connectionFinished) 0112 { 0113 if (connectionFinished) { 0114 m_segments.removeAll(segment); 0115 segment->deleteLater(); 0116 } 0117 Q_EMIT finishedSegment(this, segmentNum, connectionFinished); 0118 } 0119 0120 void MultiSegKioDataSource::setSupposedSize(KIO::filesize_t supposedSize) 0121 { 0122 m_supposedSize = supposedSize; 0123 0124 // check if the size is correct 0125 slotTotalSize(m_size); 0126 } 0127 0128 void MultiSegKioDataSource::slotTotalSize(KIO::filesize_t size, const QPair<int, int> &range) 0129 { 0130 qCDebug(KGET_DEBUG) << "Size found for" << m_sourceUrl << size << "bytes"; 0131 0132 m_size = size; 0133 0134 // findFileSize was called 0135 if ((range.first != -1) && (range.second != -1)) { 0136 Q_EMIT foundFileSize(this, size, range); 0137 } 0138 0139 // the filesize is not what it should be, maybe using a wrong mirror 0140 if (m_size && m_supposedSize && (m_size != m_supposedSize)) { 0141 qCDebug(KGET_DEBUG) << "Size does not match for" << m_sourceUrl << this; 0142 Q_EMIT broken(this, WrongDownloadSize); 0143 } 0144 } 0145 0146 void MultiSegKioDataSource::slotCanResume() 0147 { 0148 qCDebug(KGET_DEBUG) << this; 0149 0150 if (!m_canResume) { 0151 m_canResume = true; 0152 setCapabilities(capabilities() | Transfer::Cap_Resuming); 0153 } 0154 } 0155 0156 int MultiSegKioDataSource::currentSegments() const 0157 { 0158 return m_segments.count(); 0159 } 0160 0161 Segment *MultiSegKioDataSource::mostUnfinishedSegments(int *unfin) const 0162 { 0163 int unfinished = 0; 0164 Segment *seg = nullptr; 0165 foreach (Segment *segment, m_segments) { 0166 if (segment->countUnfinishedSegments() > unfinished) { 0167 unfinished = segment->countUnfinishedSegments(); 0168 seg = segment; 0169 } 0170 } 0171 0172 if (unfin) { 0173 *unfin = unfinished; 0174 } 0175 0176 return seg; 0177 } 0178 0179 int MultiSegKioDataSource::countUnfinishedSegments() const 0180 { 0181 int unfinished = 0; 0182 mostUnfinishedSegments(&unfinished); 0183 0184 return unfinished; 0185 } 0186 0187 QPair<int, int> MultiSegKioDataSource::split() 0188 { 0189 QPair<int, int> unassigned = qMakePair(-1, -1); 0190 Segment *seg = mostUnfinishedSegments(); 0191 if (seg) { 0192 unassigned = seg->split(); 0193 } 0194 0195 return unassigned; 0196 } 0197 0198 QPair<int, int> MultiSegKioDataSource::removeConnection() 0199 { 0200 QPair<int, int> unassigned = qMakePair(-1, -1); 0201 Segment *seg = mostUnfinishedSegments(); 0202 if (seg) { 0203 unassigned = seg->assignedSegments(); 0204 m_segments.removeAll(seg); 0205 seg->deleteLater(); 0206 } 0207 0208 return unassigned; 0209 } 0210 0211 bool MultiSegKioDataSource::tryMerge(const QPair<KIO::fileoffset_t, KIO::fileoffset_t> &segmentSize, const QPair<int, int> &segmentRange) 0212 { 0213 foreach (Segment *segment, m_segments) { 0214 if (segment->merge(segmentSize, segmentRange)) { 0215 return true; 0216 } 0217 } 0218 0219 return false; 0220 } 0221 0222 void MultiSegKioDataSource::slotError(Segment *segment, const QString &errorText, Transfer::LogLevel logLevel) 0223 { 0224 qCDebug(KGET_DEBUG) << "Error" << errorText << "segment" << segment; 0225 0226 const QPair<KIO::fileoffset_t, KIO::fileoffset_t> size = segment->segmentSize(); 0227 const QPair<int, int> range = segment->assignedSegments(); 0228 m_segments.removeAll(segment); 0229 segment->deleteLater(); 0230 0231 Q_EMIT log(errorText, logLevel); 0232 if (m_segments.isEmpty()) { 0233 qCDebug(KGET_DEBUG) << this << "has broken segments."; 0234 Q_EMIT brokenSegments(this, range); 0235 } else { 0236 // decrease the number of maximum parallel downloads, maybe the server does not support so many connections 0237 if (m_parallelSegments > 1) { 0238 --m_parallelSegments; 0239 } 0240 qCDebug(KGET_DEBUG) << this << "reducing connections to" << m_parallelSegments << "and freeing range of segments" << range; 0241 if (!tryMerge(size, range)) { 0242 Q_EMIT freeSegments(this, range); 0243 } 0244 } 0245 } 0246 0247 void MultiSegKioDataSource::slotFinishedDownload(KIO::filesize_t size) 0248 { 0249 stop(); 0250 Q_EMIT finishedDownload(this, size); 0251 } 0252 0253 void MultiSegKioDataSource::slotRestartBrokenSegment() 0254 { 0255 qCDebug(KGET_DEBUG) << this; 0256 start(); 0257 } 0258 0259 #include "moc_multisegkiodatasource.cpp"