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

« back to all changes in this revision

Viewing changes to joschy-snapshot-23-02-10/Provider/YouTube/src/youtubeprovider.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) 2010  Kai Dombrowe <just89@gmx.de>
 
3
 
 
4
    This library is free software; you can redistribute it and/or
 
5
    modify it under the terms of the GNU Lesser General Public
 
6
    License as published by the Free Software Foundation; either
 
7
    version 2.1 of the License, or (at your option) any later version.
 
8
 
 
9
    This library 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 GNU
 
12
    Lesser General Public License for more details.
 
13
 
 
14
    You should have received a copy of the GNU Lesser General Public
 
15
    License along with this library; if not, write to the Free Software
 
16
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
17
*/
 
18
 
 
19
 
 
20
// own
 
21
#include "youtubeprovider.h"
 
22
#include "responseparser.h"
 
23
 
 
24
// Joschy
 
25
#include <joschycore/abstractnetworklayer.h>
 
26
#include <joschycore/joschy_global.h>
 
27
#include <joschycore/video.h>
 
28
#include <joschycore/postfile.h>
 
29
#include <joschycore/abstractjob.h>
 
30
 
 
31
// Qt
 
32
#include <QtCore/QUrl>
 
33
#include <QtCore/QFile>
 
34
#include <QtCore/QDir>
 
35
 
 
36
 
 
37
 
 
38
 
 
39
#define DEV_KEY "AI39si4PPp_RmxGSVs4cHH93rcG2e9vSRQU1vC0L3sfuy_ZHmtaAWZOdvSfBjmow3YSZfrerx"\
 
40
                "jhsZGX0brUrdSLr5qvNchxeiQ"
 
41
 
 
42
 
 
43
namespace Joschy {
 
44
 
 
45
 
 
46
YouTubeProvider::YouTubeProvider(QObject *parent)
 
47
    :Joschy::AbstractProvider(parent)
 
48
{
 
49
 
 
50
 
 
51
}
 
52
 
 
53
 
 
54
YouTubeProvider::~YouTubeProvider()
 
55
{
 
56
 
 
57
    qDeleteAll(m_parser);
 
58
 
 
59
}
 
60
 
 
61
 
 
62
QStringList YouTubeProvider::categorys() const
 
63
{
 
64
 
 
65
/*    QStringList list;
 
66
    list << "Autos" << "Comedy" << "Education" << "Entertainment" << "Film";
 
67
    list << "Games" << "Howto" << "Music" << "News" << "Nonprofit";
 
68
    list << "People" << "Animals" << "Tech" << "Sports" << "Travel";
 
69
 
 
70
    return list;
 
71
*/
 
72
    return m_categorys.values();
 
73
 
 
74
}
 
75
 
 
76
 
 
77
bool YouTubeProvider::isAuthenticated(const QString &login) const
 
78
{
 
79
 
 
80
    return m_tokens.contains(login);
 
81
 
 
82
}
 
83
 
 
84
 
 
85
Joschy::ActionReply YouTubeProvider::authenticate(const QString &login, const QString &password)
 
86
{
 
87
 
 
88
    QString errorString;
 
89
    if (login.isEmpty()) {
 
90
        errorString = tr("Empty login");
 
91
    }
 
92
 
 
93
    if (password.isEmpty()) {
 
94
        errorString = tr("Empty password");
 
95
    }
 
96
 
 
97
    Joschy::ActionReply reply;
 
98
    if (!errorString.isEmpty()) {
 
99
        reply.setErrorType(Plugin::InvalidArgumentError);
 
100
        reply.setErrorString(errorString);
 
101
        return reply;
 
102
    }
 
103
 
 
104
    const QUrl url("https://www.google.com/youtube/accounts/ClientLogin");
 
105
 
 
106
    QHash<QByteArray, QByteArray> meta;
 
107
    meta.insert("Content-Type", "application/x-www-form-urlencoded");
 
108
    meta.insert("errorPage", "false");
 
109
    meta.insert("cookies", "none");
 
110
 
 
111
    const QByteArray data = "Email="+login.toLatin1()+"&Passwd="+password.toLatin1()+"&service="\
 
112
                            "youtube&source=libjoschyyoutube";
 
113
 
 
114
    const QString id = layer()->post(url, meta, data);
 
115
    m_actions.insert(id, ResponseParser::AuthenticationType);
 
116
 
 
117
    reply.setId(id);
 
118
    return reply;
 
119
 
 
120
}
 
121
 
 
122
 
 
123
Joschy::ActionReply YouTubeProvider::upload(const QString &login, Joschy::Video *video)
 
124
{
 
125
 
 
126
    Joschy::ActionReply reply;
 
127
 
 
128
    if (!isAuthenticated(login)) {
 
129
        reply.setErrorType(Plugin::NotAuthenticatedError);
 
130
        reply.setErrorString(tr("You need to authenticate first"));
 
131
    }
 
132
 
 
133
    QFile file(video->url().toString());
 
134
    if (!file.exists()) {
 
135
        reply.setErrorType(Plugin::FileNotFoundError);
 
136
        reply.setErrorString(tr("Video %1: No such file or directory").arg(video->url().toString()));
 
137
    }
 
138
 
 
139
    if (!file.open(QIODevice::ReadOnly)) {
 
140
        reply.setErrorType(Plugin::CannotOpenError);
 
141
        reply.setErrorString(file.errorString());
 
142
    }
 
143
 
 
144
    if (reply.error()) {
 
145
        return reply;
 
146
    }
 
147
 
 
148
 
 
149
    const bool bigVideo = (file.size() > (1024*1024)*10);
 
150
    const QString title = video->title();
 
151
    const QString description = video->description();
 
152
    const QString category = m_categorys.key(video->category());
 
153
    const QString keywords = video->keywords().join(", ");
 
154
    const QString slug = file.fileName().right(file.fileName().lastIndexOf(QDir::separator())+1);
 
155
    const QUrl url("http://uploads.gdata.youtube.com/feeds/api/users/"+login+"/uploads");
 
156
    const QByteArray mime = "application/octet-stream"; // TODO
 
157
 
 
158
 
 
159
    const QByteArray CRLF = "\r\n";
 
160
    const QByteArray BOUNDARY = "f93dcbA3";
 
161
    QString XML = "<?xml version=\"1.0\"?>"+CRLF+\
 
162
                     "<entry xmlns=\"http://www.w3.org/2005/Atom\""+CRLF+\
 
163
                       "xmlns:media=\"http://search.yahoo.com/mrss/\""+CRLF+\
 
164
                       "xmlns:yt=\"http://gdata.youtube.com/schemas/2007\">"+CRLF+\
 
165
                       "<media:group>"+CRLF+\
 
166
                         "<media:title type=\"plain\">%1</media:title>"+CRLF+\
 
167
                         "<media:description type=\"plain\">"+CRLF+\
 
168
                           "%2."+CRLF+\
 
169
                         "</media:description>"+CRLF+\
 
170
                         "<media:category"+CRLF+\
 
171
                           "scheme=\"http://gdata.youtube.com/schemas/2007/categories.cat\">%3"+CRLF+\
 
172
                         "</media:category>"+CRLF+\
 
173
                         "<media:keywords>%4</media:keywords>"+CRLF+\
 
174
                       "</media:group>"+CRLF+\
 
175
                     "</entry>";
 
176
 
 
177
    XML = XML.arg(title).arg(description).arg(category).arg(keywords);
 
178
 
 
179
    QByteArray postData;
 
180
    postData.append("--"+BOUNDARY);
 
181
    postData.append(CRLF);
 
182
    postData.append("Content-Type: application/atom+xml; charset=UTF-8");
 
183
    postData.append(CRLF);
 
184
    postData.append(CRLF);
 
185
    postData.append(XML.toLatin1());
 
186
    postData.append(CRLF);
 
187
    postData.append("--"+BOUNDARY);
 
188
    postData.append(CRLF);
 
189
    postData.append("Content-Type: "+mime);
 
190
    postData.append(CRLF);
 
191
    postData.append("Content-Transfer-Encoding: binary");
 
192
    postData.append(CRLF);
 
193
    postData.append(CRLF);
 
194
 
 
195
    QByteArray bottom = CRLF;
 
196
    bottom.append("--"+BOUNDARY+"--");
 
197
    bottom.append(CRLF);
 
198
 
 
199
    if (!bigVideo) {
 
200
        postData.append(file.readAll());
 
201
        postData.append(bottom);
 
202
    }
 
203
    file.close();
 
204
 
 
205
    QHash<QByteArray, QByteArray> header;
 
206
    header["Authorization"] = "GoogleLogin auth="+m_tokens[login].toLatin1();
 
207
    header["GData-Version"] = "2";
 
208
    header["X-GData-Key"] = "key="+QString(DEV_KEY).toLatin1();
 
209
    header["Connection"] = "close";
 
210
    header["Slug"] = slug.toLatin1();
 
211
    header["Content-Type"] = "multipart/related; boundary=\""+BOUNDARY+"\"";
 
212
 
 
213
    QString id;
 
214
    if (!bigVideo) {
 
215
        header["Content-Length"] = QString::number(postData.size()).toLatin1();
 
216
 
 
217
        id = layer()->post(url, header, postData);
 
218
    } else {
 
219
        Joschy::PostFile *data = new Joschy::PostFile(file.fileName());
 
220
        data->setData(postData, bottom);
 
221
 
 
222
        header["Content-Length"] = QString::number(data->size()).toLatin1();
 
223
 
 
224
        id = layer()->post(url, header, data);
 
225
        m_postFiles[id] = data;
 
226
    }
 
227
    m_actions.insert(id, ResponseParser::UploadType);
 
228
    reply.setId(id);
 
229
 
 
230
    return reply;
 
231
 
 
232
}
 
233
 
 
234
 
 
235
Joschy::ActionReply YouTubeProvider::search(const QHash<QString, QVariant> &data)
 
236
{
 
237
 
 
238
    Joschy::ActionReply reply;
 
239
 
 
240
    int start = 1;
 
241
    if (data.contains("Start")) {
 
242
        start = data.value("Start").toInt();
 
243
    }
 
244
 
 
245
    int max = 25;
 
246
    if (data.contains("Max")) {
 
247
        max = data.value("Max").toInt();
 
248
    }
 
249
 
 
250
    const QString key = data.value("Key").toString();
 
251
    const QString author = data.value("Author").toString();
 
252
    const QString category = data.value("Category").toString();
 
253
 
 
254
    if (start < 1 || max > 50 || key.isEmpty()) {
 
255
        reply.setErrorType(Plugin::InvalidArgumentError);
 
256
        reply.setErrorString(tr("Invalid argument"));
 
257
        return reply;
 
258
    }
 
259
 
 
260
    QUrl url("http://gdata.youtube.com/feeds/api/videos");
 
261
    url.addQueryItem("q", key);
 
262
    url.addQueryItem("start-index", QString::number(start));
 
263
    url.addQueryItem("max-results", QString::number(max));
 
264
 
 
265
    if (!author.isEmpty()) {
 
266
        url.addQueryItem("author", author);
 
267
    }
 
268
 
 
269
    if (!category.isEmpty()) {
 
270
        url.addQueryItem("category", category);
 
271
    }
 
272
 
 
273
 
 
274
    JOSCHY_DEBUG() << "url:" << url;
 
275
 
 
276
    reply.setId(layer()->get(url));
 
277
 
 
278
    m_actions[reply.id()] = ResponseParser::SearchType;
 
279
 
 
280
    return reply;
 
281
 
 
282
}
 
283
 
 
284
 
 
285
Joschy::ActionReply YouTubeProvider::updateThumbnail(const Joschy::Video &video, const QString &thumbnailDir)
 
286
{
 
287
 
 
288
    Joschy::ActionReply reply;
 
289
 
 
290
    if (video.thumbnailUrl().isEmpty()) {
 
291
        reply.setErrorType(Plugin::InvalidArgumentError);
 
292
        reply.setErrorString(tr("No thumbnail url given"));
 
293
        return reply;
 
294
    }
 
295
 
 
296
    QString dir = QDir::cleanPath(thumbnailDir);
 
297
    if (!dir.endsWith(QDir::separator())) {
 
298
        dir.append(QDir::separator());
 
299
    }
 
300
 
 
301
    const QString id = layer()->get(video.thumbnailUrl());
 
302
    m_actions[id] = ResponseParser::UpdateThumbnailType;
 
303
    m_thumbnails[id] = dir+video.thumbnail();
 
304
 
 
305
    reply.setId(id);
 
306
 
 
307
    return reply;
 
308
 
 
309
}
 
310
 
 
311
 
 
312
void YouTubeProvider::init()
 
313
{
 
314
 
 
315
    QVariant var = load("YouTube-Categorys");
 
316
    QHashIterator<QString, QVariant> it(var.toHash());
 
317
    while (it.hasNext()) {
 
318
        it.next();
 
319
        m_categorys[it.key()] = it.value().toString();
 
320
    }
 
321
 
 
322
    var = load("YouTube-CategoryDate");
 
323
    QDate date = var.toDateTime().date();
 
324
 
 
325
    if (m_categorys.isEmpty() || date.month() != QDate::currentDate().month()) { // update every month
 
326
        JOSCHY_DEBUG() << "update categorys....";
 
327
        updateCategorys();
 
328
    }
 
329
 
 
330
}
 
331
 
 
332
 
 
333
void YouTubeProvider::updateCategorys()
 
334
{
 
335
 
 
336
    const QUrl url("http://gdata.youtube.com/schemas/2007/categories.cat");
 
337
 
 
338
    QHash<QByteArray, QByteArray> header;
 
339
    header["Accept-Language"] = QLocale::system().name().toLatin1()+";";
 
340
 
 
341
    const QString id = layer()->get(url, header);
 
342
    m_actions[id] = ResponseParser::UpdateCategorysType;
 
343
 
 
344
}
 
345
 
 
346
 
 
347
void YouTubeProvider::parserFinished(Joschy::AbstractJob *job)
 
348
{
 
349
 
 
350
    JOSCHY_DEBUG() << "parser finsihed....";
 
351
 
 
352
    Joschy::ResponseParser *parser = static_cast<Joschy::ResponseParser*>(job);
 
353
    const QString id = parser->id();
 
354
    const ResponseParser::Type type = m_actions.take(id);
 
355
    const QString errorString = parser->errorString();
 
356
    const Plugin::ErrorType errorType = parser->errorType();
 
357
    const bool hasError = parser->error();
 
358
 
 
359
 
 
360
    if (hasError) {
 
361
        emit error(id, errorType, errorString);
 
362
    } else {
 
363
        switch (type) {
 
364
        case ResponseParser::AuthenticationType: {
 
365
                m_tokens.insert(parser->login(), parser->token());
 
366
                emit authenticated(id);
 
367
                break;
 
368
            }
 
369
        case ResponseParser::UploadType: {
 
370
                emit uploadFinished(id, parser->getVideo());
 
371
                break;
 
372
            }
 
373
        case ResponseParser::SearchType: {
 
374
                QList<Joschy::Video> videos = parser->getVideos();
 
375
                emit searchFinished(id, videos);
 
376
                break;
 
377
            }
 
378
        case ResponseParser::UpdateThumbnailType: {
 
379
                const QString thumbnail = m_thumbnails.take(id);
 
380
 
 
381
                QFile file(thumbnail);
 
382
                if (!file.open(QIODevice::WriteOnly)) {
 
383
                    JOSCHY_DEBUG() << "open failed:" << thumbnail << file.errorString();
 
384
                    emit error(id, Plugin::CannotOpenError, file.errorString());
 
385
                } else {
 
386
                    if (file.write(parser->image()) == -1) {
 
387
                        file.close();
 
388
                        JOSCHY_DEBUG() << file.error() << file.errorString();
 
389
                        emit error(id, Plugin::UnknownError, file.errorString());
 
390
                    } else {
 
391
                        file.close();
 
392
                        emit thumbnailUpdated(id);
 
393
                    }
 
394
                }
 
395
                break;
 
396
            }
 
397
        case ResponseParser::UpdateCategorysType: {
 
398
                m_categorys = parser->getCategorys();
 
399
 
 
400
                QHash<QString, QVariant> hash;
 
401
                QHashIterator<QString, QString> it(m_categorys);
 
402
                while (it.hasNext()) {
 
403
                    it.next();
 
404
                    hash[it.key()] = it.value();
 
405
                }
 
406
 
 
407
                save("YouTube-Categorys", QVariant(hash));
 
408
                save("YouTube-CategoryDate", QVariant(QDateTime::currentDateTime()));
 
409
 
 
410
                emit categorysChanged(categorys());
 
411
                break;
 
412
            }
 
413
        default: break;
 
414
        }
 
415
    }
 
416
    m_parser.removeAll(parser);
 
417
    delete parser;
 
418
    layer()->freeId(id);
 
419
 
 
420
}
 
421
 
 
422
 
 
423
void YouTubeProvider::jobFinished(const QString &id, const QVariantMap &data,
 
424
                                  const Joschy::Plugin::ErrorType &errorType,
 
425
                                  const QString &errorString)
 
426
{
 
427
 
 
428
    JOSCHY_DEBUG() << "job finished";
 
429
    JOSCHY_DEBUG() << "Reply:" << data.value("Reply").toString();
 
430
    JOSCHY_DEBUG() << "Content type:" << data.value("ContentType").toString();
 
431
    JOSCHY_DEBUG() << "Status:" << data.value("Status").toString();
 
432
    JOSCHY_DEBUG() << "error" << bool(errorType != Plugin::NoError);
 
433
 
 
434
    if (m_postFiles.contains(id)) {
 
435
        Joschy::PostFile *file = m_postFiles.take(id);
 
436
        delete file;
 
437
    }
 
438
 
 
439
    if (data.value("Canceled").toBool() || errorType == Joschy::Plugin::ActionCanceledError) {
 
440
        m_actions.remove(id);
 
441
        emit error(id, Plugin::ActionCanceledError, QString());
 
442
        return;
 
443
    }
 
444
 
 
445
/*    if (errorType != Joschy::Plugin::NoError) {
 
446
        m_actions.remove(id);
 
447
        emit error(id, errorType, errorString);
 
448
        return;
 
449
    }
 
450
*/
 
451
    Joschy::ResponseParser *parser = new Joschy::ResponseParser(m_actions.value(id), id, data, this);
 
452
    connect(parser, SIGNAL(finished(Joschy::AbstractJob*)), this, SLOT(parserFinished(Joschy::AbstractJob*)));
 
453
 
 
454
    m_parser.append(parser);
 
455
    switch (m_actions.value(id)) {
 
456
    case ResponseParser::SearchType: parser->setRunInThread(true); break;
 
457
    case ResponseParser::UpdateThumbnailType: parser->setPriority(AbstractJob::LowPriority); break;
 
458
    case ResponseParser::UpdateCategorysType: parser->setPriority(AbstractJob::HighPriority); break;
 
459
    default: break;
 
460
    }
 
461
 
 
462
    Joschy::Scheduler::schedule(parser);
 
463
 
 
464
}
 
465
 
 
466
 
 
467
 
 
468
} // namespace Joschy
 
469
 
 
470
 
 
471
#include "youtubeprovider.moc"