~ubuntu-branches/ubuntu/raring/recorditnow/raring

« back to all changes in this revision

Viewing changes to src/plugins/upload/lib/youtubeservice.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-01-09 14:54:01 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20110109145401-gyckb4airz4fio50
Tags: 0.8.1-0ubuntu1
* New upstream release. (LP: #681270)
  - Update debian/copyright.
* Build-depend on recordmydesktop.
* Add a watch file.
* Drop 01_fix_ftbfs_kwarning_call.diff, fixed upstream.
* Add 01_joschy_install_to_usr_lib.diff.
* Add 02_fix_ftbfs_no-add-needed.diff.
* Add 03_dont_install_header_files.diff.
* Replace dependency on libpolkit-qt-1-0 with policykit-1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/***************************************************************************ß
2
 
 *   Copyright (C) 2009 by Kai Dombrowe <just89@gmx.de>                    *
3
 
 *                                                                         *
4
 
 *   This program is free software; you can redistribute it and/or modify  *
5
 
 *   it under the terms of the GNU General Public License as published by  *
6
 
 *   the Free Software Foundation; either version 2 of the License, or     *
7
 
 *   (at your option) any later version.                                   *
8
 
 *                                                                         *
9
 
 *   This program is distributed in the hope that it will be useful,       *
10
 
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11
 
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12
 
 *   GNU General Public License for more details.                          *
13
 
 *                                                                         *
14
 
 *   You should have received a copy of the GNU General Public License     *
15
 
 *   along with this program; if not, write to the                         *
16
 
 *   Free Software Foundation, Inc.,                                       *
17
 
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
18
 
 ***************************************************************************/
19
 
 
20
 
// own
21
 
#include "youtubeservice.h"
22
 
 
23
 
// KDE
24
 
#include <kdebug.h>
25
 
#include <kurl.h>
26
 
#include <kmimetype.h>
27
 
#include <klocalizedstring.h>
28
 
 
29
 
// Qt
30
 
#include <QtCore/QFile>
31
 
#include <QtXml/QXmlStreamReader>
32
 
#include <QtCore/QDateTime>
33
 
 
34
 
 
35
 
#define DEV_KEY "AI39si4PPp_RmxGSVs4cHH93rcG2e9vSRQU1vC0L3sfuy_ZHmtaAWZOdvSfBjmow3YSZfrerx"\
36
 
                "jhsZGX0brUrdSLr5qvNchxeiQ"
37
 
 
38
 
YouTubeService::YouTubeService(QObject *parent)
39
 
    : KYouBlip::Service(parent)
40
 
{
41
 
 
42
 
    m_authenticated = false;
43
 
 
44
 
}
45
 
 
46
 
 
47
 
YouTubeService::~YouTubeService()
48
 
{
49
 
 
50
 
}
51
 
 
52
 
 
53
 
bool YouTubeService::isAuthenticated(const QString &account) const
54
 
{
55
 
 
56
 
    return !m_token[account].isEmpty();
57
 
 
58
 
}
59
 
 
60
 
 
61
 
QString YouTubeService::authenticate(const QString &account, const QString &password)
62
 
{
63
 
 
64
 
    if (account.isEmpty() || password.isEmpty()) {
65
 
        return "Error: "+i18n("No account/password specified.");
66
 
    }
67
 
 
68
 
    const KUrl url("https://www.google.com/youtube/accounts/ClientLogin");
69
 
 
70
 
    KIO::MetaData meta;
71
 
    meta.insert("content-type", "Content-Type: application/x-www-form-urlencoded");
72
 
    meta.insert("errorPage", "false");
73
 
 
74
 
    QByteArray postData = "Email="+account.toLatin1()+"&Passwd="+password.toLatin1()+"&service="\
75
 
                          "youtube&source=RecordItNow";
76
 
 
77
 
    const QString id = Service::getUniqueId();
78
 
    m_accountIds[id] = account;
79
 
 
80
 
    m_jobs[post(url, meta, postData, true)] = qMakePair(AuthJob, account);
81
 
 
82
 
    return id;
83
 
 
84
 
}
85
 
 
86
 
 
87
 
QString YouTubeService::upload(const YouTubeVideo *video, const QString &account)
88
 
{
89
 
 
90
 
    QString errorString;
91
 
    if (!isAuthenticated(account)) {
92
 
        errorString = i18n("Please authenticate first!");
93
 
    }
94
 
 
95
 
    const QString videoFile = video->file();
96
 
    const QString title = video->title();
97
 
    const QString description = video->description();
98
 
    const QString category = video->getCategory();
99
 
    const QString tags = video->keywords().join(", ");
100
 
 
101
 
    int index = videoFile.lastIndexOf('/');
102
 
    index != -1 ? index++ : index = 0;
103
 
    QString fileName = videoFile.mid(index);
104
 
 
105
 
    if (!QFile::exists(videoFile)) {
106
 
        errorString = i18n("No such file: %1.", videoFile);
107
 
    }
108
 
 
109
 
    if (title.length() > 60 || title.toLatin1().size() > 100) {
110
 
        errorString = i18n("Title too long.");
111
 
    }
112
 
 
113
 
    if (description.length() > 5000) {
114
 
        errorString = i18n("Description too long.");
115
 
    }
116
 
 
117
 
    if (tags.length() > 120) {
118
 
        errorString = i18n("Too long tags.");
119
 
    }
120
 
 
121
 
    if (title.isEmpty()) {
122
 
        errorString = i18n("No title specified.");
123
 
    }
124
 
 
125
 
    if (description.isEmpty()) {
126
 
        errorString = i18n("No description specified.");
127
 
    }
128
 
 
129
 
    if (tags.isEmpty()) {
130
 
        errorString = i18n("No tags specified.");
131
 
    }
132
 
 
133
 
    if (category == "-5") {
134
 
        kDebug() << "category" << category;
135
 
        errorString = i18n("Invalid category.");
136
 
    }
137
 
 
138
 
    foreach (const QString &keyword, tags.split(',')) {
139
 
        if (keyword.length() > 25) {
140
 
            errorString = i18n("Tag \"%1\" is too long.", keyword);
141
 
            break;
142
 
        } else if (keyword.length() < 2) {
143
 
            errorString = i18n("Tag \"%1\" is too short.", keyword);
144
 
            break;
145
 
        }
146
 
    }
147
 
 
148
 
    if (!errorString.isEmpty()) {
149
 
        return "Error: "+errorString;
150
 
    }
151
 
 
152
 
    const KUrl url("http://uploads.gdata.youtube.com/feeds/api/users/"+account+"/uploads");
153
 
 
154
 
    QByteArray CRLF = "\r\n";
155
 
    QByteArray BOUNDARY = "f93dcbA3";
156
 
 
157
 
    QFile file(videoFile);
158
 
    if (!file.open(QIODevice::ReadOnly)) {
159
 
        emit error(i18n("Cannot open video!"), account);
160
 
        return QString ();
161
 
    }
162
 
    QByteArray videoData = file.readAll();
163
 
    file.close();
164
 
 
165
 
    KMimeType::Ptr type = KMimeType::findByUrl(KUrl(videoFile));
166
 
    QByteArray mime = type->name().toLatin1();
167
 
    if (mime.isEmpty()) {
168
 
        mime = "application/octet-stream";
169
 
    }
170
 
 
171
 
    QString XML = "<?xml version=\"1.0\"?>"+CRLF+\
172
 
                     "<entry xmlns=\"http://www.w3.org/2005/Atom\""+CRLF+\
173
 
                       "xmlns:media=\"http://search.yahoo.com/mrss/\""+CRLF+\
174
 
                       "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">"+CRLF+\
175
 
                       "<media:group>"+CRLF+\
176
 
                         "<media:title type=\"plain\">%1</media:title>"+CRLF+\
177
 
                         "<media:description type=\"plain\">"+CRLF+\
178
 
                           "%2."+CRLF+\
179
 
                         "</media:description>"+CRLF+\
180
 
                         "<media:category"+CRLF+\
181
 
                           "scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">%3"+CRLF+\
182
 
                         "</media:category>"+CRLF+\
183
 
                         "<media:keywords>%4</media:keywords>"+CRLF+\
184
 
                       "</media:group>"+CRLF+\
185
 
                     "</entry>";
186
 
 
187
 
    XML = XML.arg(title).arg(description).arg(category).arg(tags);
188
 
 
189
 
    QByteArray postData;
190
 
    postData.append("--"+BOUNDARY);
191
 
    postData.append(CRLF);
192
 
    postData.append("Content-Type: application/atom+xml; charset=UTF-8");
193
 
    postData.append(CRLF);
194
 
    postData.append(CRLF);
195
 
    postData.append(XML.toLatin1());
196
 
    postData.append(CRLF);
197
 
    postData.append("--"+BOUNDARY);
198
 
    postData.append(CRLF);
199
 
    postData.append("Content-Type: "+mime);
200
 
    postData.append(CRLF);
201
 
    postData.append("Content-Transfer-Encoding: binary");
202
 
    postData.append(CRLF);
203
 
    postData.append(CRLF);
204
 
    postData.append(videoData);
205
 
    postData.append(CRLF);
206
 
    postData.append("--"+BOUNDARY+"--");
207
 
    postData.append(CRLF);
208
 
 
209
 
 
210
 
    const QString id = Service::getUniqueId();
211
 
    m_accountIds[id] = account;
212
 
 
213
 
    QHash<QString, QString> header;
214
 
    header["Authorization"] = "GoogleLogin auth="+m_token[account].toLatin1();
215
 
    header["GData-Version"] = '2';
216
 
    header["X-GData-Key"] = "key="+QString(DEV_KEY).toLatin1();
217
 
    header["Connection"] = "close";
218
 
    header["Slug"] = fileName.toLatin1();
219
 
    header["Content-Type"] = "multipart/related; boundary=\""+BOUNDARY+"\"";
220
 
    header["Content-Length"] = QString::number(postData.size()).toLatin1();
221
 
 
222
 
    InfoJob *job = post(url, header, postData);
223
 
    m_jobs[job] = qMakePair(UploadJob, id);
224
 
    job->setSource(video->file());
225
 
 
226
 
    return id;
227
 
 
228
 
}
229
 
 
230
 
 
231
 
QString YouTubeService::search(const QString &key, const QString &author, const int &start,
232
 
                               const int &max)
233
 
{
234
 
 
235
 
    if (start < 1) {
236
 
        return QString();
237
 
    }
238
 
 
239
 
    if (max > 50) {
240
 
        return QString();
241
 
    }
242
 
 
243
 
    const QString id = Service::getUniqueId();
244
 
 
245
 
    KUrl url("http://gdata.youtube.com/feeds/api/videos");
246
 
    url.addQueryItem("q", key);
247
 
    url.addQueryItem("start-index", QString::number(start));
248
 
    url.addQueryItem("max-results", QString::number(max));
249
 
 
250
 
    if (!author.isEmpty()) {
251
 
        url.addQueryItem("author", author);
252
 
    }
253
 
 
254
 
    m_jobs[get(url, KIO::NoReload, true)] = qMakePair(SearchJob, id);
255
 
    return id;
256
 
 
257
 
}
258
 
 
259
 
 
260
 
QString YouTubeService::getFavorites(const QString &user, const int &max)
261
 
{
262
 
 
263
 
    const QString id = Service::getUniqueId();
264
 
    KUrl url("http://gdata.youtube.com/feeds/api/users/"+user+"/favorites?v=2");
265
 
    url.addQueryItem("max-results", QString::number(max));
266
 
    m_jobs[get(url, KIO::NoReload, true)] = qMakePair(FavoritesJob, id);
267
 
    return id;
268
 
 
269
 
}
270
 
 
271
 
 
272
 
void YouTubeService::cancelUpload()
273
 
{
274
 
 
275
 
    QHashIterator<KJob*, JobData> it(m_jobs);
276
 
    while (it.hasNext()) {
277
 
        it.next();
278
 
        if (it.key()) {
279
 
            it.key()->kill();
280
 
        }
281
 
    }
282
 
 
283
 
}
284
 
 
285
 
 
286
 
void YouTubeService::cancelUpload(const QString &id)
287
 
{
288
 
 
289
 
    QHashIterator<KJob*, JobData> it(m_jobs);
290
 
    while (it.hasNext()) {
291
 
        it.next();
292
 
        if (it.key() && it.value().second == id) {
293
 
            it.key()->kill();
294
 
        }
295
 
    }
296
 
 
297
 
}
298
 
 
299
 
 
300
 
void YouTubeService::jobFinished(KJob *job, const QByteArray &data)
301
 
{
302
 
 
303
 
    JobData jData = m_jobs[job];
304
 
    QString id = jData.second;
305
 
    const JobType type = jData.first;
306
 
    const int ret = job->error();
307
 
 
308
 
    m_jobs.remove(job);
309
 
 
310
 
    kDebug() << "job finished:" << type << id << data;
311
 
 
312
 
    QString response = data.trimmed();
313
 
 
314
 
    switch (type) {
315
 
    case AuthJob: {
316
 
 
317
 
            const QString key = id;
318
 
            id = m_accountIds.key(key);
319
 
            m_accountIds.remove(key);
320
 
 
321
 
            if (response.startsWith(QLatin1String("Auth="))) {
322
 
                response.remove("Auth=");
323
 
 
324
 
                const QStringList lines = response.split('\n');
325
 
                QString user = lines.last();
326
 
                user.remove(QRegExp(".*="));
327
 
                m_token[user] = lines.first();
328
 
                emit authenticated(id);
329
 
            } else if (ret == KIO::ERR_USER_CANCELED) {
330
 
                emit canceled(id);
331
 
            } else {
332
 
                emit error(i18n("Authentication failed!"), id);
333
 
            }
334
 
            break;
335
 
        }
336
 
    case UploadJob: {
337
 
 
338
 
            const QString key = id;
339
 
            id = m_accountIds.key(key);
340
 
            m_accountIds.remove(key);
341
 
 
342
 
            YouTubeVideo *video = 0;
343
 
            QXmlStreamReader reader(response);
344
 
            while (!reader.atEnd()) {
345
 
                reader.readNext();
346
 
                if (reader.isStartElement() && reader.name() == "entry") {
347
 
                    video = readEntry(&reader);
348
 
                }
349
 
            }
350
 
            if ((reader.hasError() || !video) && ret != KIO::ERR_USER_CANCELED) {
351
 
                emit error(i18nc("%1 = error", "Upload failed!\n"
352
 
                                 "Response: %1", response), id);
353
 
                break;
354
 
            }
355
 
            emit uploadFinished(video, id);
356
 
            break;
357
 
        }
358
 
    case FavoritesJob:
359
 
    case SearchJob: {
360
 
            QList<YouTubeVideo*> videoList;
361
 
            QXmlStreamReader reader(data);
362
 
            while (!reader.atEnd()) {
363
 
                reader.readNext();
364
 
                if (reader.isStartElement()) {
365
 
                    if (reader.name() == "feed") {
366
 
                        while (!reader.atEnd()) {
367
 
                            reader.readNext();
368
 
                            if (reader.isStartElement() && reader.name() == "entry") {
369
 
                                videoList.append(readEntry(&reader));
370
 
                            }
371
 
                        }
372
 
                    }
373
 
                }
374
 
            }
375
 
            if (type == SearchJob) {
376
 
                emit searchFinished(videoList, id);
377
 
            } else {
378
 
                emit favoritesFinished(videoList, id);
379
 
            }
380
 
            break;
381
 
        }
382
 
    default: break;
383
 
    }
384
 
 
385
 
}
386
 
 
387
 
 
388
 
YouTubeVideo *YouTubeService::readEntry(QXmlStreamReader *reader)
389
 
{
390
 
 
391
 
    YouTubeVideo *video = new YouTubeVideo(this);
392
 
    while (!reader->atEnd()) {
393
 
        reader->readNext();
394
 
 
395
 
        if (reader->isEndElement() && reader->name() == "entry") {
396
 
            break;
397
 
        }
398
 
 
399
 
        if (reader->isStartElement()) {
400
 
            if (reader->name() == "link"
401
 
                && reader->attributes().value("rel").toString() == "alternate"
402
 
                && reader->attributes().value("type").toString() == "text/html") {
403
 
                QString webpage = reader->attributes().value("href").toString();
404
 
                video->setUrl(KUrl(webpage));
405
 
            } else if (reader->name() == "author") {
406
 
                reader->readNext();
407
 
                if (reader->name() == "name") {
408
 
                    video->setAuthor(reader->readElementText());
409
 
                }
410
 
            } else if (reader->name() == "published") {
411
 
                video->setPublished(QDateTime::fromString(reader->readElementText(), Qt::ISODate));
412
 
            } else if (reader->namespaceUri() == "http://gdata.youtube.com/schemas/2007"
413
 
                       && reader->name() == "statistics") {
414
 
                video->setViewCount(reader->attributes().value("viewCount").toString().toInt());
415
 
                video->setFavoriteCount(reader->attributes().value("favoriteCount").toString().toInt());
416
 
            }
417
 
            else if (reader->namespaceUri() == "http://search.yahoo.com/mrss/"
418
 
                     && reader->name() == "group") {
419
 
 
420
 
                // media group
421
 
                while (!reader->atEnd()) {
422
 
                    reader->readNext();
423
 
                    if (reader->isEndElement() && reader->name() == "group") {
424
 
                        break;
425
 
                    }
426
 
                    if (reader->isStartElement()) {
427
 
                        if (reader->name() == "thumbnail") {
428
 
                            video->setThumbnailUrl(KUrl(reader->attributes().value("url").toString()));
429
 
                        } else if (reader->name() == "title") {
430
 
                            QString title = reader->readElementText();
431
 
                            video->setTitle(title);
432
 
                        } else if (reader->name() == "description") {
433
 
                            QString desc = reader->readElementText();
434
 
                            video->setDescription(desc);
435
 
                        } else if (reader->name() == "duration") {
436
 
                            QString duration = reader->attributes().value("seconds").toString();
437
 
                            video->setDuration(duration.toInt());
438
 
                        } else if (reader->name() == "keywords") {
439
 
                            video->setKeywords(reader->readElementText());
440
 
                        } else if (reader->name() == "category") {
441
 
                            video->setCategory(reader->readElementText());
442
 
                        }
443
 
                    }
444
 
                }
445
 
            } else if (reader->name() == "rating") {
446
 
                video->setRating(reader->attributes().value("average").toString().toDouble());
447
 
                video->setRaters(reader->attributes().value("numRaters").toString().toInt());
448
 
            }
449
 
        }
450
 
    }
451
 
    return video;
452
 
 
453
 
}
454
 
 
455
 
 
456
 
#include "youtubeservice.moc"
457