File indexing completed on 2024-05-12 12:53:18
0001 /* 0002 This file is part of Choqok, the KDE micro-blogging client 0003 0004 SPDX-FileCopyrightText: 2014 Eugene Shalygin <eugene.shalygin@gmail.com> 0005 0006 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #include "longurl.h" 0010 0011 #include <QDebug> 0012 #include <QJsonDocument> 0013 #include <QSharedPointer> 0014 #include <QTimer> 0015 0016 #include <KIO/TransferJob> 0017 #include <KPluginFactory> 0018 0019 #include "postwidget.h" 0020 #include "shortenmanager.h" 0021 0022 K_PLUGIN_CLASS_WITH_JSON(LongUrl, "choqok_longurl.json") 0023 0024 const QString baseLongUrlDorComUrl = QLatin1String("http://api.longurl.org/v2/"); 0025 0026 LongUrl::LongUrl(QObject *parent, const QList< QVariant > &) 0027 : Choqok::Plugin(QLatin1String("choqok_longurl"), parent) 0028 , state(Stopped), mServicesAreFetched(false) 0029 { 0030 sheduleSupportedServicesFetch(); 0031 connect(Choqok::UI::Global::SessionManager::self(), 0032 SIGNAL(newPostWidgetAdded(Choqok::UI::PostWidget*,Choqok::Account*,QString)), 0033 this, 0034 SLOT(slotAddNewPostWidget(Choqok::UI::PostWidget*))); 0035 } 0036 0037 LongUrl::~LongUrl() 0038 { 0039 suspendJobs(); 0040 mData.clear(); 0041 mShortUrls.clear(); 0042 for (KJob *job: mParsingList.keys()) { 0043 job->kill(); 0044 } 0045 mParsingList.clear(); 0046 } 0047 0048 void LongUrl::parse(QPointer< Choqok::UI::PostWidget > postToParse) 0049 { 0050 if (!postToParse) { 0051 return; 0052 } 0053 QStringList redirectList, pureList = postToParse->urls(); 0054 QString content = postToParse->currentPost()->content; 0055 for (int i = 0; i < pureList.count(); ++i) { 0056 if (pureList[i].length() > 30) { 0057 continue; 0058 } 0059 if (!pureList[i].startsWith(QLatin1String("http"), Qt::CaseInsensitive)) { 0060 pureList[i].prepend(QLatin1String("http://")); 0061 } 0062 redirectList << pureList[i]; 0063 } 0064 for (const QString &url: redirectList) { 0065 KJob *job = sheduleParsing(url); 0066 if (job) { 0067 mParsingList.insert(job, postToParse); 0068 job->start(); 0069 } 0070 } 0071 } 0072 0073 void LongUrl::processJobResults(KJob *job) 0074 { 0075 const QJsonDocument json = QJsonDocument::fromJson(mData[job]); 0076 if (json.isNull()) { 0077 return; 0078 } 0079 const QVariantMap m = json.toVariant().toMap(); 0080 const QUrl longUrl = m.value(QLatin1String("long-url")).toUrl(); 0081 replaceUrl(takeJob(job), QUrl(mShortUrls.take(job)), longUrl); 0082 } 0083 0084 void LongUrl::startParsing() 0085 { 0086 int i = 8; 0087 while (!postsQueue.isEmpty() && i > 0) { 0088 parse(postsQueue.dequeue()); 0089 --i; 0090 } 0091 0092 if (postsQueue.isEmpty()) { 0093 state = Stopped; 0094 } else { 0095 QTimer::singleShot(500, this, SLOT(startParsing())); 0096 } 0097 } 0098 0099 void LongUrl::replaceUrl(LongUrl::PostWidgetPointer post, const QUrl &fromUrl, const QUrl &toUrl) 0100 { 0101 if (post) { 0102 QString content = post->content(); 0103 QString fromUrlStr = fromUrl.url(); 0104 content.replace(QRegExp(QLatin1String("title='") + fromUrlStr + QLatin1Char('\'')), QLatin1String("title='") + toUrl.url() + QLatin1Char('\'')); 0105 content.replace(QRegExp(QLatin1String("href='") + fromUrlStr + QLatin1Char('\'')), QLatin1String("href='") + toUrl.url() + QLatin1Char('\'')); 0106 post->setContent(content); 0107 Choqok::ShortenManager::self()->emitNewUnshortenedUrl(post, fromUrl, toUrl); 0108 } 0109 } 0110 0111 void LongUrl::sheduleSupportedServicesFetch() 0112 { 0113 mServicesAreFetched = true; 0114 mServicesData = QSharedPointer<QByteArray>(new QByteArray()); 0115 KIO::TransferJob *job = KIO::get(QUrl(baseLongUrlDorComUrl + QLatin1String("services?format=json")), KIO::NoReload, KIO::HideProgressInfo); 0116 connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(servicesDataReceived(KIO::Job*,QByteArray))); 0117 connect(job, SIGNAL(result(KJob*)), SLOT(servicesJobResult(KJob*))); 0118 } 0119 0120 void LongUrl::servicesDataReceived(KIO::Job *job, QByteArray data) 0121 { 0122 Q_UNUSED(job); 0123 mServicesData->append(data); 0124 } 0125 0126 void LongUrl::servicesJobResult(KJob *job) 0127 { 0128 if (!job->error()) { 0129 const QJsonDocument json = QJsonDocument::fromJson(*mServicesData); 0130 if (!json.isNull()) { 0131 supportedServices = json.toVariant().toMap().uniqueKeys(); 0132 } 0133 } else { 0134 qCritical() << "Job Error:" << job->errorString(); 0135 } 0136 mServicesAreFetched = false; 0137 mServicesData.clear(); 0138 } 0139 0140 bool LongUrl::isServiceSupported(const QString &host) 0141 { 0142 return supportedServices.contains(host); 0143 } 0144 0145 KJob *LongUrl::sheduleParsing(const QString &shortUrl) 0146 { 0147 QUrl url(shortUrl); 0148 if (isServiceSupported(url.host())) { 0149 QUrl request = QUrl(baseLongUrlDorComUrl + QLatin1String("expand")); 0150 QUrlQuery requestQuery; 0151 requestQuery.addQueryItem(QLatin1String("url"), url.url()); 0152 requestQuery.addQueryItem(QLatin1String("format"), QLatin1String("json")); 0153 requestQuery.addQueryItem(QLatin1String("user-agent"), QLatin1String("Choqok")); 0154 request.setQuery(requestQuery); 0155 0156 KIO::TransferJob *job = KIO::get(request, KIO::NoReload, KIO::HideProgressInfo); 0157 mData.insert(job, QByteArray()); 0158 mShortUrls.insert(job, shortUrl); 0159 connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(dataReceived(KIO::Job*,QByteArray))); 0160 connect(job, SIGNAL(result(KJob*)), SLOT(jobResult(KJob*))); 0161 return job; 0162 } 0163 return 0; 0164 } 0165 0166 void LongUrl::dataReceived(KIO::Job *job, QByteArray data) 0167 { 0168 mData[job].append(data); 0169 } 0170 0171 void LongUrl::jobResult(KJob *job) 0172 { 0173 if (!job->error()) { 0174 processJobResults(job); 0175 } 0176 mData.remove(job); 0177 mShortUrls.remove(job); 0178 mParsingList.remove(job); 0179 } 0180 0181 void LongUrl::slotAddNewPostWidget(Choqok::UI::PostWidget *newWidget) 0182 { 0183 postsQueue.enqueue(newWidget); 0184 if (state == Stopped && !mServicesAreFetched) { 0185 state = Running; 0186 QTimer::singleShot(1000, this, SLOT(startParsing())); 0187 } 0188 } 0189 0190 void LongUrl::aboutToUnload() 0191 { 0192 suspendJobs(); 0193 base::aboutToUnload(); 0194 } 0195 0196 void LongUrl::suspendJobs() 0197 { 0198 for (KJob *job: mParsingList.keys()) { 0199 job->suspend(); 0200 } 0201 } 0202 0203 #include "longurl.moc" 0204 #include "moc_longurl.cpp"