1 /*
2 This file is part of Choqok, the KDE micro-blogging client
3
4 Based on the imagepreview extension
5 Copyright (C) 2010-2012 Emanuele Bigiarini <pulmro@gmail.com>
6 Copyright (C) 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of
11 the License or (at your option) version 3 or any later version
12 accepted by the membership of KDE e.V. (or its successor approved
13 by the membership of KDE e.V.), which shall act as a proxy
14 defined in Section 14 of version 3 of the license.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, see http://www.gnu.org/licenses/
23
24 */
25 #include "videopreview.h"
26
27 #include <QDebug>
28 #include <QDomDocument>
29 #include <QDomElement>
30 #include <QEventLoop>
31 #include <QTimer>
32
33 #include <KIO/StoredTransferJob>
34 #include <KJobWidgets>
35 #include <KPluginFactory>
36
37 #include "choqokuiglobal.h"
38 #include "postwidget.h"
39 #include "notifymanager.h"
40 #include "mediamanager.h"
41 #include "textbrowser.h"
42 #include "shortenmanager.h"
43
44 K_PLUGIN_FACTORY_WITH_JSON(VideoPreviewFactory, "choqok_videopreview.json",
45 registerPlugin < VideoPreview > ();)
46
47 const QRegExp VideoPreview::mYouTuRegExp(QLatin1String("(https?://youtu.[^\\s<>\"]+[^!,\\.\\s<>'\\\"\\]])"));
48 const QRegExp VideoPreview::mYouTubeRegExp(QLatin1String("(https?://www.youtube.[^\\s<>\"]+[^!,\\.\\s<>'\\\"\\]])"));
49 const QRegExp VideoPreview::mVimeoRegExp(QLatin1String("(https?://(.+)?vimeo.com/(.+)[&]?)"));
50
51 const QRegExp VideoPreview::mYouTuCode(QLatin1String("youtu.(.+)/(.+)[?&]?"));
52
VideoPreview(QObject * parent,const QList<QVariant> &)53 VideoPreview::VideoPreview(QObject *parent, const QList< QVariant > &)
54 : Choqok::Plugin(QLatin1String("choqok_videopreview"), parent)
55 , state(Stopped)
56 {
57 connect(Choqok::UI::Global::SessionManager::self(), &Choqok::UI::Global::SessionManager::newPostWidgetAdded,
58 this, &VideoPreview::slotAddNewPostWidget);
59 connect(Choqok::ShortenManager::self(), &Choqok::ShortenManager::newUnshortenedUrl,
60 this, &VideoPreview::slotNewUnshortenedUrl);
61 }
62
~VideoPreview()63 VideoPreview::~VideoPreview()
64 {
65
66 }
67
slotAddNewPostWidget(Choqok::UI::PostWidget * newWidget)68 void VideoPreview::slotAddNewPostWidget(Choqok::UI::PostWidget *newWidget)
69 {
70 postsQueue.enqueue(newWidget);
71 if (state == Stopped) {
72 state = Running;
73 QTimer::singleShot(1000, this, SLOT(startParsing()));
74 }
75 }
76
slotNewUnshortenedUrl(Choqok::UI::PostWidget * widget,const QUrl & fromUrl,const QUrl & toUrl)77 void VideoPreview::slotNewUnshortenedUrl(Choqok::UI::PostWidget *widget, const QUrl &fromUrl, const QUrl &toUrl)
78 {
79 Q_UNUSED(fromUrl)
80 if (mYouTubeRegExp.indexIn(toUrl.toDisplayString()) != -1) {
81 QUrl thisurl(mYouTubeRegExp.cap(0));
82 QUrlQuery thisurlQuery(thisurl);
83 QUrl thumbUrl = parseYoutube(thisurlQuery.queryItemValue(QLatin1String("v")), widget);
84 connect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched,
85 this, &VideoPreview::slotImageFetched);
86 Choqok::MediaManager::self()->fetchImage(thumbUrl, Choqok::MediaManager::Async);
87 } else if (mVimeoRegExp.indexIn(toUrl.toDisplayString()) != -1) {
88 QUrl thumbUrl = parseVimeo(mVimeoRegExp.cap(3), widget);
89 connect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched,
90 this, &VideoPreview::slotImageFetched);
91 Choqok::MediaManager::self()->fetchImage(thumbUrl, Choqok::MediaManager::Async);
92 }
93
94 }
95
startParsing()96 void VideoPreview::startParsing()
97 {
98 int i = 8;
99 while (!postsQueue.isEmpty() && i > 0) {
100 parse(postsQueue.dequeue());
101 --i;
102 }
103
104 if (postsQueue.isEmpty()) {
105 state = Stopped;
106 } else {
107 QTimer::singleShot(500, this, SLOT(startParsing()));
108 }
109 }
110
parse(QPointer<Choqok::UI::PostWidget> postToParse)111 void VideoPreview::parse(QPointer<Choqok::UI::PostWidget> postToParse)
112 {
113 if (!postToParse) {
114 return;
115 }
116 int pos = 0;
117 int pos1 = 0;
118 int pos2 = 0;
119 int pos3 = 0;
120 QList<QUrl> thumbList;
121
122 QString content = postToParse->currentPost()->content;
123
124 while (((pos1 = mYouTuRegExp.indexIn(content, pos)) != -1) |
125 ((pos2 = mYouTubeRegExp.indexIn(content, pos)) != -1) |
126 ((pos3 = mVimeoRegExp.indexIn(content, pos)) != -1)) {
127
128 if (pos1 >= 0) {
129 pos = pos1 + mYouTuRegExp.matchedLength();
130 if (mYouTuCode.indexIn(mYouTuRegExp.cap(0)) != -1) {
131 thumbList << parseYoutube(mYouTuCode.cap(2), postToParse);
132 }
133 } else if (pos2 >= 0) {
134 pos = pos2 + mYouTubeRegExp.matchedLength();
135 QUrl thisurl(mYouTubeRegExp.cap(0));
136 QUrlQuery thisurlQuery(thisurl);
137 thumbList << parseYoutube(thisurlQuery.queryItemValue(QLatin1String("v")), postToParse);
138 } else if (pos3 >= 0) {
139 pos = pos3 + mVimeoRegExp.matchedLength();
140 thumbList << parseVimeo(mVimeoRegExp.cap(3), postToParse);
141 }
142 }
143
144 for (const QUrl &thumb_url: thumbList) {
145 connect(Choqok::MediaManager::self(), &Choqok::MediaManager::imageFetched,
146 this, &VideoPreview::slotImageFetched);
147
148 Choqok::MediaManager::self()->fetchImage(thumb_url, Choqok::MediaManager::Async);
149 }
150
151 }
152
parseYoutube(QString videoid,QPointer<Choqok::UI::PostWidget> postToParse)153 QUrl VideoPreview::parseYoutube(QString videoid, QPointer< Choqok::UI::PostWidget > postToParse)
154 {
155 QString youtubeUrl = QStringLiteral("https://gdata.youtube.com/feeds/api/videos/%1").arg(videoid);
156 QUrl th_url(youtubeUrl);
157 KIO::StoredTransferJob *job = KIO::storedGet(th_url, KIO::NoReload, KIO::HideProgressInfo);
158 KJobWidgets::setWindow(job, Choqok::UI::Global::mainWindow());
159 QString title, description;
160 QUrl thumb_url;
161
162 job->exec();
163 if (!job->error()) {
164 QDomDocument document;
165 document.setContent(job->data());
166 QDomElement root = document.documentElement();
167 if (!root.isNull()) {
168 QDomElement node;
169 node = root.firstChildElement(QLatin1String("title"));
170 if (!node.isNull()) {
171 title = QString(node.text());
172 }
173 node = root.firstChildElement(QLatin1String("media:group"));
174 node = node.firstChildElement(QLatin1String("media:description"));
175 if (!node.isNull()) {
176 description = QString(node.text());
177 }
178
179 node = node.nextSiblingElement(QLatin1String("media:thumbnail"));
180 if (!node.isNull()) {
181 thumb_url = QUrl::fromUserInput(node.attributeNode(QLatin1String("url")).value());
182 }
183 }
184
185 description = description.left(70);
186
187 mParsingList.insert(thumb_url, postToParse);
188 mBaseUrlMap.insert(thumb_url, QLatin1String("https://www.youtube.com/watch?v=") + videoid);
189 mTitleVideoMap.insert(thumb_url, title);
190 mDescriptionVideoMap.insert(thumb_url, description);
191 } else {
192 qCritical() << "Youtube XML response is NULL!";
193 }
194
195 return thumb_url;
196 }
197
parseVimeo(QString videoid,QPointer<Choqok::UI::PostWidget> postToParse)198 QUrl VideoPreview::parseVimeo(QString videoid, QPointer< Choqok::UI::PostWidget > postToParse)
199 {
200 QString vimeoUrl = QStringLiteral("https://vimeo.com/api/v2/video/%1.xml").arg(videoid);
201 QUrl th_url(vimeoUrl);
202 QEventLoop loop;
203 KIO::StoredTransferJob *job = KIO::storedGet(th_url, KIO::NoReload, KIO::HideProgressInfo);
204 KJobWidgets::setWindow(job, Choqok::UI::Global::mainWindow());
205 QString title, description;
206 QUrl thumb_url;
207
208 job->exec();
209 if (!job->error()) {
210 QDomDocument document;
211 document.setContent(job->data());
212 QDomElement root = document.documentElement();
213 if (!root.isNull()) {
214 QDomElement videotag;
215 videotag = root.firstChildElement(QLatin1String("video"));
216 if (!videotag.isNull()) {
217 QDomElement node;
218 node = videotag.firstChildElement(QLatin1String("title"));
219 if (!node.isNull()) {
220 title = QString(node.text());
221 }
222 node = videotag.firstChildElement(QLatin1String("description"));
223 if (!node.isNull()) {
224 description = QString(node.text());
225 }
226 node = videotag.firstChildElement(QLatin1String("thumbnail_small"));
227 if (!node.isNull()) {
228 thumb_url = QUrl::fromUserInput(node.text());
229 }
230 }
231 }
232 description = description.left(70);
233
234 mParsingList.insert(thumb_url, postToParse);
235 mBaseUrlMap.insert(thumb_url, QLatin1String("https://vimeo.com/") + videoid);
236 mTitleVideoMap.insert(thumb_url, title);
237 mDescriptionVideoMap.insert(thumb_url, description);
238 } else {
239 qCritical() << "Vimeo XML response is NULL!";
240 }
241
242 return thumb_url;
243 }
244
slotImageFetched(const QUrl & remoteUrl,const QPixmap & pixmap)245 void VideoPreview::slotImageFetched(const QUrl &remoteUrl, const QPixmap &pixmap)
246 {
247 Choqok::UI::PostWidget *postToParse = mParsingList.take(remoteUrl);
248 QString baseUrl = mBaseUrlMap.take(remoteUrl);
249 QString title = mTitleVideoMap.take(remoteUrl);
250 QString description = mDescriptionVideoMap.take(remoteUrl);
251
252 if (!postToParse) {
253 return;
254 }
255 QString content = postToParse->content();
256 QUrl imgU(remoteUrl);
257 imgU.setScheme(QLatin1String("img"));
258 postToParse->mainWidget()->document()->addResource(QTextDocument::ImageResource, imgU, pixmap);
259
260 QString temp(QLatin1String("<br/><table><tr><td rowspan=2><img align='left' height=64 src='")
261 + imgU.toDisplayString() + QLatin1String("' /></td>"));
262 temp.append(QLatin1String("<td><a href='") + baseUrl + QLatin1String("' title='") + baseUrl + QLatin1String("'><b>") + title + QLatin1String("</b></a></td></tr>"));
263 temp.append(QLatin1String("<tr><font size=\"-1\">") + description + QLatin1String("</font></tr></table>"));
264
265 content.append(temp);
266 postToParse->setContent(content);
267 }
268
269 #include "videopreview.moc"
270