~vanvugt/ubuntu/oneiric/mediatomb/fix-770964-784431

« back to all changes in this revision

Viewing changes to src/youtube_content_handler.cc

  • Committer: Bazaar Package Importer
  • Author(s): Andres Mejia
  • Date: 2009-04-22 21:39:19 UTC
  • mto: (4.2.1 sid)
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: james.westby@ubuntu.com-20090422213919-52m015y6gcpv1m1g
Tags: upstream-0.12.0~svn2018
ImportĀ upstreamĀ versionĀ 0.12.0~svn2018

Show diffs side-by-side

added added

removed removed

Lines of Context:
7
7
    Copyright (C) 2005 Gena Batyan <bgeradz@mediatomb.cc>,
8
8
                       Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>
9
9
    
10
 
    Copyright (C) 2006-2008 Gena Batyan <bgeradz@mediatomb.cc>,
 
10
    Copyright (C) 2006-2009 Gena Batyan <bgeradz@mediatomb.cc>,
11
11
                            Sergey 'Jin' Bostandzhyan <jin@mediatomb.cc>,
12
12
                            Leonhard Wimmer <leo@mediatomb.cc>
13
13
    
24
24
    version 2 along with MediaTomb; if not, write to the Free Software
25
25
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
26
26
    
27
 
    $Id: youtube_content_handler.cc 1698 2008-02-23 20:48:30Z lww $
 
27
    $Id: youtube_content_handler.cc 2010 2009-01-11 19:10:43Z lww $
28
28
*/
29
29
 
30
30
/// \file youtube_content_handler.cc
43
43
#include "cds_objects.h"
44
44
#include "config_manager.h"
45
45
 
 
46
#define YT_SWF_TYPE "application/x-shockwave-flash"
 
47
#define YT_MP4_TYPE "video/mp4"
 
48
 
46
49
using namespace zmm;
47
50
using namespace mxml;
48
51
 
50
53
{
51
54
    String temp;
52
55
 
53
 
    if (service->getName() != "ut_response")
 
56
    if (service->getName() != "rss")
54
57
        throw _Exception(_("Invalid XML for YouTube service received"));
55
58
 
56
 
    temp = service->getAttribute(_("status"));
57
 
 
58
 
    if (temp != "ok")
 
59
    Ref<Element> channel = service->getChildByName(_("channel"));
 
60
    if (channel == nil)
 
61
        throw _Exception(_("Invalid XML for YouTube service received - channel not found!"));
 
62
 
 
63
    this->service_xml = channel;
 
64
 
 
65
    feed_name = channel->getChildText(_("title"));
 
66
 
 
67
    if (!string_ok(feed_name))
 
68
        throw _Exception(_("Invalid XML for YouTube service, received - missing feed title!"));
 
69
 
 
70
    temp = service_xml->getChildText(_("openSearch:totalResults")); 
 
71
    if (temp.toInt() == 0)
59
72
        return false;
60
73
 
61
 
    Ref<Element> video_list = service->getChildByName(_("video_list"));
62
 
    if (video_list == nil)
63
 
        throw _Exception(_("Invalid XML for YouTube service received - video_list not found!"));
64
 
 
65
 
    this->service_xml = video_list;
66
 
 
67
 
    video_list_child_count = service_xml->elementChildCount();
 
74
    channel_child_count = service_xml->childCount();
68
75
    
69
 
    if (video_list_child_count == 0)
 
76
    if (channel_child_count == 0)
70
77
        return false;
71
78
 
72
 
    if (video_list_child_count == 1)
 
79
    // doh... I liked the old rest API a lot more.. nevertheless, we need
 
80
    // to figure out when to stop fetching stuff
 
81
    bool has_items = false;
 
82
    int item_node_index = 0;
 
83
    while (item_node_index < channel_child_count)
73
84
    {
74
 
        if (service_xml->getFirstElementChild()->getName() == _("total"))
 
85
        Ref<Node> n = service_xml->getChild(item_node_index);
 
86
        if (n == nil)
75
87
            return false;
 
88
        
 
89
        item_node_index++;
 
90
 
 
91
        if (n->getType() != mxml_node_element)
 
92
            continue;
 
93
 
 
94
        Ref<Element> channel_item = RefCast(n, Element);
 
95
        if (channel_item->getName() == "item")
 
96
        {
 
97
            has_items = true;
 
98
            break;
 
99
        }
76
100
    }
77
101
 
78
 
    current_video_node_index = 0;
79
 
 
80
 
    Ref<Dictionary> mappings = ConfigManager::getInstance()->getDictionaryOption(CFG_IMPORT_MAPPINGS_MIMETYPE_TO_CONTENTTYPE_LIST);
 
102
    // no item tags found, so on more videos here - we are done
 
103
    if (!has_items)
 
104
        return false;
 
105
 
 
106
    current_node_index = 0;
 
107
 
 
108
    Ref<Dictionary> mappings = ConfigManager::getInstance()->getDictionaryOption(CFG_IMPORT_MAPPINGS_EXTENSION_TO_MIMETYPE_LIST);
81
109
 
82
110
    // this is somewhat a dilemma... we know that YT video thumbs are jpeg
83
111
    // but we do not check that; we could probably do a HTTP HEAD request,
84
112
    // however that would cause quite some network activity since there may
85
 
    // be hundreds and thousands of those items
86
 
    thumb_mimetype = mappings->get(_(CONTENT_TYPE_JPG));
 
113
    // be hundreds and thousands of those items, we will have a look at the
 
114
    // extension of the URL, this should be enough.
 
115
    thumb_mimetype = mappings->get(_("jpg"));
87
116
    if (!string_ok(thumb_mimetype))
88
117
        thumb_mimetype = _("image/jpeg");
89
118
 
 
119
    mp4_mimetype = mappings->get(_("mp4"));
 
120
    if (!string_ok(mp4_mimetype))
 
121
        mp4_mimetype = _("video/mp4");
 
122
 
90
123
    return true;
91
124
}
92
125
 
 
126
Ref<YouTubeSubFeed> YouTubeContentHandler::getSubFeed(Ref<Element> feedxml)
 
127
{
 
128
    String temp;
 
129
     Ref<YouTubeSubFeed> subfeed(new YouTubeSubFeed());
 
130
 
 
131
    if (feedxml->getName() != "rss")
 
132
        throw _Exception(_("Invalid XML for YouTube feed received"));
 
133
 
 
134
    Ref<Element> channel = feedxml->getChildByName(_("channel"));
 
135
    if (channel == nil)
 
136
        throw _Exception(_("Invalid XML for YouTube service received - channel not found!"));
 
137
   
 
138
    subfeed->title = channel->getChildText(_("title"));
 
139
    if (!string_ok(subfeed->title))
 
140
        throw _Exception(_("Invalid XML for YouTube service, received - missing feed title!"));
 
141
 
 
142
    temp = channel->getChildText(_("openSearch:totalResults"));
 
143
    if (temp.toInt() == 0)
 
144
        return subfeed;
 
145
 
 
146
 
 
147
    // now cycle through all items and put the links into the array
 
148
    for (int i = 0; i < channel->childCount(); i++)
 
149
    {
 
150
        Ref<Node> n = channel->getChild(i);
 
151
        if (n->getType() != mxml_node_element)
 
152
            continue;
 
153
 
 
154
        Ref<Element> channel_item = RefCast(n, Element);
 
155
        if (channel_item->getName() != "item")
 
156
            continue;
 
157
 
 
158
        Ref<Element> link = channel_item->getChildByName(_("gd:feedLink"));
 
159
        if (link == nil)
 
160
            continue;
 
161
        temp = link->getAttribute(_("href"));
 
162
        if (!string_ok(temp))
 
163
            continue;
 
164
 
 
165
        subfeed->links->append(temp);
 
166
    }
 
167
 
 
168
    return subfeed;
 
169
}
 
170
 
93
171
Ref<CdsObject> YouTubeContentHandler::getNextObject()
94
172
{
95
173
#define DATE_BUF_LEN 12
96
174
    String temp;
97
 
    time_t epoch;
98
175
    struct tm t;
99
176
    char datebuf[DATE_BUF_LEN];
100
177
    struct timespec ts;
101
178
 
102
 
    while (current_video_node_index < video_list_child_count)
 
179
    while (current_node_index < channel_child_count)
103
180
    {
104
 
        Ref<Element> video = service_xml->getElementChild(current_video_node_index);
105
 
        current_video_node_index++;
106
 
       
107
 
        if (video == nil)
 
181
        Ref<Node> n = service_xml->getChild(current_node_index);
 
182
 
 
183
        current_node_index++;
 
184
      
 
185
        if (n == nil)
 
186
            return nil;
 
187
 
 
188
        if (n->getType() != mxml_node_element)
108
189
            continue;
109
190
 
110
 
        if (video->getName() != "video")
 
191
        Ref<Element> channel_item = RefCast(n, Element);
 
192
        if (channel_item->getName() != "item")
111
193
            continue;
112
194
 
113
195
        // we know what we are adding
114
196
        Ref<CdsItemExternalURL> item(new CdsItemExternalURL());
115
197
        Ref<CdsResource> resource(new CdsResource(CH_DEFAULT));
 
198
        item->addResource(resource);
116
199
        resource->addParameter(_(ONLINE_SERVICE_AUX_ID), 
117
 
                               String::from(OS_YouTube));
118
 
 
119
 
        item->setAuxData(_(ONLINE_SERVICE_AUX_ID),
120
200
                String::from(OS_YouTube));
121
201
 
122
 
        temp = video->getChildText(_("id"));
 
202
        item->setAuxData(_(ONLINE_SERVICE_AUX_ID), String::from(OS_YouTube));
 
203
 
 
204
        temp = channel_item->getChildText(_("link")); 
 
205
        /// \todo create an own class for items that fetch the URL on request
 
206
        /// and to not store it permanently
123
207
        if (!string_ok(temp))
124
208
        {
125
209
            log_warning("Failed to retrieve YouTube video ID\n");
126
210
            continue;
127
211
        }
 
212
            
 
213
        item->setURL(temp);
 
214
 
 
215
        int eq = temp.rindex('=');
 
216
        if (eq > 0)
 
217
            temp = temp.substring(eq + 1);
 
218
 
128
219
 
129
220
        item->setClass(_("object.item.videoItem"));
130
 
        /// \todo create an own class for items that fetch the URL on request
131
 
        /// and to not store it permanently
132
 
        item->setURL(_(" "));
133
221
        temp = String(OnlineService::getStoragePrefix(OS_YouTube)) + temp;
134
222
        item->setServiceID(temp);
135
223
 
136
 
        temp = video->getChildText(_("title"));
137
 
        if (string_ok(temp))
138
 
            item->setTitle(temp);
139
 
        else
140
 
            item->setTitle(_("Unknown"));
141
 
 
142
 
        item->setMimeType(_("video/x-flv"));
143
 
        resource->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO),
144
 
                renderProtocolInfo(_("video/x-flv")));
145
 
        
146
 
        temp = video->getChildText(_("length_in_seconds"));
147
 
        if (string_ok(temp))
148
 
        {
149
 
            resource->addAttribute(MetadataHandler::getResAttrName(R_DURATION),
150
 
                                   secondsToHMS(temp.toInt()));
151
 
        }
152
 
       
153
 
        temp = video->getChildText(_("description"));
154
 
        if (string_ok(temp))
155
 
            item->setMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION),
156
 
                              temp);
157
 
 
158
 
        temp = video->getChildText(_("upload_time"));
159
 
        if (string_ok(temp))
160
 
        {
161
 
            epoch = (time_t)temp.toLong();
162
 
            t = *gmtime(&epoch);
 
224
        temp = channel_item->getChildText(_("pubDate"));
 
225
        if (string_ok(temp))
 
226
        {
163
227
            datebuf[0] = '\0';
164
 
            if (strftime(datebuf, sizeof(datebuf), "%F", &t) != 0)
 
228
            // Tue, 18 Jul 2006 17:43:47 +0000
 
229
            if (strptime(temp.c_str(),  "%a, %d %b %Y %T +000", &t) != NULL)
165
230
            {
166
 
                datebuf[DATE_BUF_LEN-1] = '\0';
167
 
                if (strlen(datebuf) > 0)
 
231
                if (strftime(datebuf, sizeof(datebuf), "%F", &t) != 0)
168
232
                {
169
 
                    item->setMetadata(MetadataHandler::getMetaFieldName(M_DATE),
 
233
                    datebuf[DATE_BUF_LEN-1] = '\0';
 
234
                    if (strlen(datebuf) > 0)
 
235
                    {
 
236
                        item->setMetadata(MetadataHandler::getMetaFieldName(M_DATE),
170
237
                            String(datebuf));
 
238
                    }
171
239
                }
172
240
            }
173
241
        }
174
242
 
175
 
        temp = video->getChildText(_("tags"));
176
 
        if (string_ok(temp))
177
 
        {
178
 
            item->setAuxData(_(YOUTUBE_AUXDATA_TAGS), temp);
179
 
        }
180
 
 
181
 
        temp = video->getChildText(_("rating_avg"));
182
 
        if (string_ok(temp))
183
 
        {
184
 
            item->setAuxData(_(YOUTUBE_AUXDATA_AVG_RATING), temp);
185
 
        }
186
 
 
187
 
        temp = video->getChildText(_("author"));
188
 
        if (string_ok(temp))
189
 
        {
 
243
 
 
244
        temp = channel_item->getChildText(_("author"));
 
245
        if (string_ok(temp))
190
246
            item->setAuxData(_(YOUTUBE_AUXDATA_AUTHOR), temp);
191
 
        }
192
 
 
193
 
        temp = video->getChildText(_("view_count"));
194
 
        if (string_ok(temp))
195
 
        {
196
 
            item->setAuxData(_(YOUTUBE_AUXDATA_VIEW_COUNT), temp);
197
 
        }
198
 
 
199
 
        temp = video->getChildText(_("comment_count"));
200
 
        if (string_ok(temp))
201
 
        {
202
 
            item->setAuxData(_(YOUTUBE_AUXDATA_COMMENT_COUNT), temp);
203
 
        }
204
 
 
205
 
        temp = video->getChildText(_("rating_count"));
206
 
        if (string_ok(temp))
207
 
        {
208
 
            item->setAuxData(_(YOUTUBE_AUXDATA_RATING_COUNT), temp);
209
 
        }
210
 
 
211
 
        item->setAuxData(_(ONLINE_SERVICE_AUX_ID), String::from(OS_YouTube));
212
 
 
213
 
        item->addResource(resource);
214
 
 
215
 
        temp = video->getChildText(_("thumbnail_url"));
216
 
        if (string_ok(temp))
217
 
        {
218
 
            Ref<CdsResource> thumbnail(new CdsResource(CH_EXTURL));
219
 
            thumbnail->
220
 
                addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO),
221
 
                        renderProtocolInfo(thumb_mimetype));
222
 
            // same as with thumb mimetype.. in most cases this resolution
223
 
            // will be correct, we could omit it but then the renderers 
224
 
            // would assume that this is a full image and thus not display
225
 
            // it as thumbnail
226
 
            thumbnail->
227
 
                addAttribute(MetadataHandler::getResAttrName(R_RESOLUTION), 
228
 
                        _("130x97"));
229
 
            thumbnail->addOption(_(RESOURCE_OPTION_URL), temp);
230
 
            thumbnail->addOption(_(RESOURCE_OPTION_PROXY_URL), _(FALSE));
231
 
            item->addResource(thumbnail);
232
 
        }
 
247
 
 
248
        Ref<Element> mediagroup = channel_item->getChildByName(_("media:group"));
 
249
        if (mediagroup == nil)
 
250
            continue;
 
251
 
 
252
        // media:group uses a couple of elements with the same name, so
 
253
        // we will cycle through adn fill it that way
 
254
        
 
255
        bool content_set = false;
 
256
 
 
257
        int mediagroup_child_count = mediagroup->elementChildCount();
 
258
        for (int mcc = 0; mcc < mediagroup_child_count; mcc++)
 
259
        {
 
260
            Ref<Element> el = mediagroup->getElementChild(mcc);
 
261
            if (el == nil)
 
262
                continue;
 
263
 
 
264
            if (el->getName() == "media:title")
 
265
            {
 
266
                temp = el->getText();
 
267
                if (string_ok(temp))
 
268
                    item->setTitle(temp);
 
269
                else
 
270
                    item->setTitle(_("Unknown"));
 
271
            }
 
272
            else if (el->getName() == "media:description")
 
273
            {
 
274
                temp = el->getText();
 
275
                if (string_ok(temp))
 
276
                    item->setMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION), temp);
 
277
            }
 
278
            else if (el->getName() == "media:keywords")
 
279
            {
 
280
                temp = el->getText();
 
281
                if (string_ok(temp))
 
282
                    item->setAuxData(_(YOUTUBE_AUXDATA_KEYWORDS), temp);
 
283
            }
 
284
            else if (el->getName() == "media:category")
 
285
            {
 
286
                temp = el->getText();
 
287
                if (string_ok(temp))
 
288
                    item->setAuxData(_(YOUTUBE_AUXDATA_CATEGORY), temp);
 
289
            }
 
290
            else if (el->getName() == "media:content")
 
291
            {
 
292
                if (content_set)
 
293
                    continue;
 
294
 
 
295
                temp = el->getAttribute(_("type"));
 
296
                if ((temp != YT_SWF_TYPE) && (temp != YT_MP4_TYPE))
 
297
                    continue;
 
298
 
 
299
                String mt;
 
300
                if (ConfigManager::getInstance()->getBoolOption(CFG_ONLINE_CONTENT_YOUTUBE_FORMAT_MP4))
 
301
                {
 
302
                    mt = mp4_mimetype;
 
303
                }
 
304
                else
 
305
                    mt = _("video/x-flv");
 
306
 
 
307
                    item->setMimeType(mt);
 
308
                    resource->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO), renderProtocolInfo(mt));
 
309
 
 
310
                content_set = true;
 
311
 
 
312
                temp = el->getAttribute(_("duration"));
 
313
                if (string_ok(temp))
 
314
                {
 
315
                    resource->addAttribute(MetadataHandler::getResAttrName(R_DURATION), secondsToHMS(temp.toInt()));
 
316
                }
 
317
            }
 
318
            else if (el->getName() == "media:thumbnail")
 
319
            {
 
320
                temp = el->getAttribute(_("url"));
 
321
                if (!string_ok(temp))
 
322
                    continue;
 
323
 
 
324
                if (temp.substring(temp.length()-3) != "jpg")
 
325
                {
 
326
                    log_warning("Found new YouTube thumbnail image type, please report this to contact at mediatomb dot cc! [%s]\n", temp.c_str());
 
327
                    continue;
 
328
                }
 
329
 
 
330
                Ref<CdsResource> thumbnail(new CdsResource(CH_EXTURL));
 
331
                thumbnail->addOption(_(RESOURCE_CONTENT_TYPE), _(THUMBNAIL));
 
332
                thumbnail->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO), renderProtocolInfo(thumb_mimetype));
 
333
                thumbnail->addOption(_(RESOURCE_OPTION_URL), temp);
 
334
            
 
335
                String temp2 = el->getAttribute(_("width"));
 
336
                temp = el->getAttribute(_("height"));
 
337
 
 
338
                if (string_ok(temp) && string_ok(temp2))
 
339
                {
 
340
                    thumbnail->addAttribute(MetadataHandler::getResAttrName(R_RESOLUTION), temp2 + "x" + temp);
 
341
                }
 
342
                else
 
343
                    continue;
 
344
 
 
345
                thumbnail->addOption(_(RESOURCE_OPTION_PROXY_URL), _(FALSE));
 
346
                item->addResource(thumbnail);
 
347
            }
 
348
 
 
349
        }
 
350
      
 
351
        Ref<Element> stats = channel_item->getChildByName(_("yt:statistics"));
 
352
        if (stats != nil)
 
353
        {
 
354
            temp = stats->getAttribute(_("viewCount"));
 
355
            if (string_ok(temp))
 
356
                item->setAuxData(_(YOUTUBE_AUXDATA_VIEW_COUNT), temp);
 
357
 
 
358
            temp = stats->getAttribute(_("favoriteCount"));
 
359
            if (string_ok(temp))
 
360
                item->setAuxData(_(YOUTUBE_AUXDATA_FAVORITE_COUNT), temp);
 
361
        }
 
362
 
 
363
        Ref<Element> rating = channel_item->getChildByName(_("gd:rating"));
 
364
        if (rating != nil)
 
365
        {
 
366
            temp = rating->getAttribute(_("average"));
 
367
            if (string_ok(temp))
 
368
                item->setAuxData(_(YOUTUBE_AUXDATA_AVG_RATING), temp);
 
369
 
 
370
            temp = rating->getAttribute(_("numRaters"));
 
371
            if (string_ok(temp))
 
372
                item->setAuxData(_(YOUTUBE_AUXDATA_RATING_COUNT), temp);
 
373
        }
 
374
 
 
375
        item->setAuxData(_(YOUTUBE_AUXDATA_FEED), feed_name);
233
376
 
234
377
        getTimespecNow(&ts);
235
378
        item->setAuxData(_(ONLINE_SERVICE_LAST_UPDATE), String::from(ts.tv_sec));
247
390
                        ex.getMessage().c_str());
248
391
            continue;
249
392
        }
250
 
 
251
 
 
252
393
    } // while
253
394
    return nil;
254
395
}
255
396
 
 
397
YouTubeSubFeed::YouTubeSubFeed()
 
398
{
 
399
    links = Ref<Array<StringBase> >(new Array<StringBase>());
 
400
    title = nil;
 
401
}
 
402
 
256
403
#endif//YOUTUBE
 
404