53
if (service->getName() != "ut_response")
56
if (service->getName() != "rss")
54
57
throw _Exception(_("Invalid XML for YouTube service received"));
56
temp = service->getAttribute(_("status"));
59
Ref<Element> channel = service->getChildByName(_("channel"));
61
throw _Exception(_("Invalid XML for YouTube service received - channel not found!"));
63
this->service_xml = channel;
65
feed_name = channel->getChildText(_("title"));
67
if (!string_ok(feed_name))
68
throw _Exception(_("Invalid XML for YouTube service, received - missing feed title!"));
70
temp = service_xml->getChildText(_("openSearch:totalResults"));
71
if (temp.toInt() == 0)
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!"));
65
this->service_xml = video_list;
67
video_list_child_count = service_xml->elementChildCount();
74
channel_child_count = service_xml->childCount();
69
if (video_list_child_count == 0)
76
if (channel_child_count == 0)
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)
74
if (service_xml->getFirstElementChild()->getName() == _("total"))
85
Ref<Node> n = service_xml->getChild(item_node_index);
91
if (n->getType() != mxml_node_element)
94
Ref<Element> channel_item = RefCast(n, Element);
95
if (channel_item->getName() == "item")
78
current_video_node_index = 0;
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
106
current_node_index = 0;
108
Ref<Dictionary> mappings = ConfigManager::getInstance()->getDictionaryOption(CFG_IMPORT_MAPPINGS_EXTENSION_TO_MIMETYPE_LIST);
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");
119
mp4_mimetype = mappings->get(_("mp4"));
120
if (!string_ok(mp4_mimetype))
121
mp4_mimetype = _("video/mp4");
126
Ref<YouTubeSubFeed> YouTubeContentHandler::getSubFeed(Ref<Element> feedxml)
129
Ref<YouTubeSubFeed> subfeed(new YouTubeSubFeed());
131
if (feedxml->getName() != "rss")
132
throw _Exception(_("Invalid XML for YouTube feed received"));
134
Ref<Element> channel = feedxml->getChildByName(_("channel"));
136
throw _Exception(_("Invalid XML for YouTube service received - channel not found!"));
138
subfeed->title = channel->getChildText(_("title"));
139
if (!string_ok(subfeed->title))
140
throw _Exception(_("Invalid XML for YouTube service, received - missing feed title!"));
142
temp = channel->getChildText(_("openSearch:totalResults"));
143
if (temp.toInt() == 0)
147
// now cycle through all items and put the links into the array
148
for (int i = 0; i < channel->childCount(); i++)
150
Ref<Node> n = channel->getChild(i);
151
if (n->getType() != mxml_node_element)
154
Ref<Element> channel_item = RefCast(n, Element);
155
if (channel_item->getName() != "item")
158
Ref<Element> link = channel_item->getChildByName(_("gd:feedLink"));
161
temp = link->getAttribute(_("href"));
162
if (!string_ok(temp))
165
subfeed->links->append(temp);
93
171
Ref<CdsObject> YouTubeContentHandler::getNextObject()
95
173
#define DATE_BUF_LEN 12
99
176
char datebuf[DATE_BUF_LEN];
100
177
struct timespec ts;
102
while (current_video_node_index < video_list_child_count)
179
while (current_node_index < channel_child_count)
104
Ref<Element> video = service_xml->getElementChild(current_video_node_index);
105
current_video_node_index++;
181
Ref<Node> n = service_xml->getChild(current_node_index);
183
current_node_index++;
188
if (n->getType() != mxml_node_element)
110
if (video->getName() != "video")
191
Ref<Element> channel_item = RefCast(n, Element);
192
if (channel_item->getName() != "item")
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));
119
item->setAuxData(_(ONLINE_SERVICE_AUX_ID),
120
200
String::from(OS_YouTube));
122
temp = video->getChildText(_("id"));
202
item->setAuxData(_(ONLINE_SERVICE_AUX_ID), String::from(OS_YouTube));
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))
125
209
log_warning("Failed to retrieve YouTube video ID\n");
215
int eq = temp.rindex('=');
217
temp = temp.substring(eq + 1);
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);
136
temp = video->getChildText(_("title"));
138
item->setTitle(temp);
140
item->setTitle(_("Unknown"));
142
item->setMimeType(_("video/x-flv"));
143
resource->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO),
144
renderProtocolInfo(_("video/x-flv")));
146
temp = video->getChildText(_("length_in_seconds"));
149
resource->addAttribute(MetadataHandler::getResAttrName(R_DURATION),
150
secondsToHMS(temp.toInt()));
153
temp = video->getChildText(_("description"));
155
item->setMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION),
158
temp = video->getChildText(_("upload_time"));
161
epoch = (time_t)temp.toLong();
224
temp = channel_item->getChildText(_("pubDate"));
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)
166
datebuf[DATE_BUF_LEN-1] = '\0';
167
if (strlen(datebuf) > 0)
231
if (strftime(datebuf, sizeof(datebuf), "%F", &t) != 0)
169
item->setMetadata(MetadataHandler::getMetaFieldName(M_DATE),
233
datebuf[DATE_BUF_LEN-1] = '\0';
234
if (strlen(datebuf) > 0)
236
item->setMetadata(MetadataHandler::getMetaFieldName(M_DATE),
170
237
String(datebuf));
175
temp = video->getChildText(_("tags"));
178
item->setAuxData(_(YOUTUBE_AUXDATA_TAGS), temp);
181
temp = video->getChildText(_("rating_avg"));
184
item->setAuxData(_(YOUTUBE_AUXDATA_AVG_RATING), temp);
187
temp = video->getChildText(_("author"));
244
temp = channel_item->getChildText(_("author"));
190
246
item->setAuxData(_(YOUTUBE_AUXDATA_AUTHOR), temp);
193
temp = video->getChildText(_("view_count"));
196
item->setAuxData(_(YOUTUBE_AUXDATA_VIEW_COUNT), temp);
199
temp = video->getChildText(_("comment_count"));
202
item->setAuxData(_(YOUTUBE_AUXDATA_COMMENT_COUNT), temp);
205
temp = video->getChildText(_("rating_count"));
208
item->setAuxData(_(YOUTUBE_AUXDATA_RATING_COUNT), temp);
211
item->setAuxData(_(ONLINE_SERVICE_AUX_ID), String::from(OS_YouTube));
213
item->addResource(resource);
215
temp = video->getChildText(_("thumbnail_url"));
218
Ref<CdsResource> thumbnail(new CdsResource(CH_EXTURL));
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
227
addAttribute(MetadataHandler::getResAttrName(R_RESOLUTION),
229
thumbnail->addOption(_(RESOURCE_OPTION_URL), temp);
230
thumbnail->addOption(_(RESOURCE_OPTION_PROXY_URL), _(FALSE));
231
item->addResource(thumbnail);
248
Ref<Element> mediagroup = channel_item->getChildByName(_("media:group"));
249
if (mediagroup == nil)
252
// media:group uses a couple of elements with the same name, so
253
// we will cycle through adn fill it that way
255
bool content_set = false;
257
int mediagroup_child_count = mediagroup->elementChildCount();
258
for (int mcc = 0; mcc < mediagroup_child_count; mcc++)
260
Ref<Element> el = mediagroup->getElementChild(mcc);
264
if (el->getName() == "media:title")
266
temp = el->getText();
268
item->setTitle(temp);
270
item->setTitle(_("Unknown"));
272
else if (el->getName() == "media:description")
274
temp = el->getText();
276
item->setMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION), temp);
278
else if (el->getName() == "media:keywords")
280
temp = el->getText();
282
item->setAuxData(_(YOUTUBE_AUXDATA_KEYWORDS), temp);
284
else if (el->getName() == "media:category")
286
temp = el->getText();
288
item->setAuxData(_(YOUTUBE_AUXDATA_CATEGORY), temp);
290
else if (el->getName() == "media:content")
295
temp = el->getAttribute(_("type"));
296
if ((temp != YT_SWF_TYPE) && (temp != YT_MP4_TYPE))
300
if (ConfigManager::getInstance()->getBoolOption(CFG_ONLINE_CONTENT_YOUTUBE_FORMAT_MP4))
305
mt = _("video/x-flv");
307
item->setMimeType(mt);
308
resource->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO), renderProtocolInfo(mt));
312
temp = el->getAttribute(_("duration"));
315
resource->addAttribute(MetadataHandler::getResAttrName(R_DURATION), secondsToHMS(temp.toInt()));
318
else if (el->getName() == "media:thumbnail")
320
temp = el->getAttribute(_("url"));
321
if (!string_ok(temp))
324
if (temp.substring(temp.length()-3) != "jpg")
326
log_warning("Found new YouTube thumbnail image type, please report this to contact at mediatomb dot cc! [%s]\n", temp.c_str());
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);
335
String temp2 = el->getAttribute(_("width"));
336
temp = el->getAttribute(_("height"));
338
if (string_ok(temp) && string_ok(temp2))
340
thumbnail->addAttribute(MetadataHandler::getResAttrName(R_RESOLUTION), temp2 + "x" + temp);
345
thumbnail->addOption(_(RESOURCE_OPTION_PROXY_URL), _(FALSE));
346
item->addResource(thumbnail);
351
Ref<Element> stats = channel_item->getChildByName(_("yt:statistics"));
354
temp = stats->getAttribute(_("viewCount"));
356
item->setAuxData(_(YOUTUBE_AUXDATA_VIEW_COUNT), temp);
358
temp = stats->getAttribute(_("favoriteCount"));
360
item->setAuxData(_(YOUTUBE_AUXDATA_FAVORITE_COUNT), temp);
363
Ref<Element> rating = channel_item->getChildByName(_("gd:rating"));
366
temp = rating->getAttribute(_("average"));
368
item->setAuxData(_(YOUTUBE_AUXDATA_AVG_RATING), temp);
370
temp = rating->getAttribute(_("numRaters"));
372
item->setAuxData(_(YOUTUBE_AUXDATA_RATING_COUNT), temp);
375
item->setAuxData(_(YOUTUBE_AUXDATA_FEED), feed_name);
234
377
getTimespecNow(&ts);
235
378
item->setAuxData(_(ONLINE_SERVICE_LAST_UPDATE), String::from(ts.tv_sec));