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"