00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00032
00033 #ifdef HAVE_CONFIG_H
00034 #include "autoconfig.h"
00035 #endif
00036
00037 #if defined(YOUTUBE)
00038
00039 #include "youtube_content_handler.h"
00040 #include "online_service.h"
00041 #include "tools.h"
00042 #include "metadata_handler.h"
00043 #include "cds_objects.h"
00044 #include "config_manager.h"
00045
00046 #define YT_SWF_TYPE "application/x-shockwave-flash"
00047 #define YT_MP4_TYPE "video/mp4"
00048
00049 using namespace zmm;
00050 using namespace mxml;
00051
00052 bool YouTubeContentHandler::setServiceContent(zmm::Ref<mxml::Element> service)
00053 {
00054 String temp;
00055
00056 if (service->getName() != "rss")
00057 throw _Exception(_("Invalid XML for YouTube service received"));
00058
00059 Ref<Element> channel = service->getChildByName(_("channel"));
00060 if (channel == nil)
00061 throw _Exception(_("Invalid XML for YouTube service received - channel not found!"));
00062
00063 this->service_xml = channel;
00064
00065 feed_name = channel->getChildText(_("title"));
00066
00067 if (!string_ok(feed_name))
00068 throw _Exception(_("Invalid XML for YouTube service, received - missing feed title!"));
00069
00070 temp = service_xml->getChildText(_("openSearch:totalResults"));
00071 if (temp.toInt() == 0)
00072 return false;
00073
00074 channel_child_count = service_xml->childCount();
00075
00076 if (channel_child_count == 0)
00077 return false;
00078
00079
00080
00081 bool has_items = false;
00082 int item_node_index = 0;
00083 while (item_node_index < channel_child_count)
00084 {
00085 Ref<Node> n = service_xml->getChild(item_node_index);
00086 if (n == nil)
00087 return false;
00088
00089 item_node_index++;
00090
00091 if (n->getType() != mxml_node_element)
00092 continue;
00093
00094 Ref<Element> channel_item = RefCast(n, Element);
00095 if (channel_item->getName() == "item")
00096 {
00097 has_items = true;
00098 break;
00099 }
00100 }
00101
00102
00103 if (!has_items)
00104 return false;
00105
00106 current_node_index = 0;
00107
00108 Ref<Dictionary> mappings = ConfigManager::getInstance()->getDictionaryOption(CFG_IMPORT_MAPPINGS_EXTENSION_TO_MIMETYPE_LIST);
00109
00110
00111
00112
00113
00114
00115 thumb_mimetype = mappings->get(_("jpg"));
00116 if (!string_ok(thumb_mimetype))
00117 thumb_mimetype = _("image/jpeg");
00118
00119 mp4_mimetype = mappings->get(_("mp4"));
00120 if (!string_ok(mp4_mimetype))
00121 mp4_mimetype = _("video/mp4");
00122
00123 return true;
00124 }
00125
00126 Ref<YouTubeSubFeed> YouTubeContentHandler::getSubFeed(Ref<Element> feedxml)
00127 {
00128 String temp;
00129 Ref<YouTubeSubFeed> subfeed(new YouTubeSubFeed());
00130
00131 if (feedxml->getName() != "rss")
00132 throw _Exception(_("Invalid XML for YouTube feed received"));
00133
00134 Ref<Element> channel = feedxml->getChildByName(_("channel"));
00135 if (channel == nil)
00136 throw _Exception(_("Invalid XML for YouTube service received - channel not found!"));
00137
00138 subfeed->title = channel->getChildText(_("title"));
00139 if (!string_ok(subfeed->title))
00140 throw _Exception(_("Invalid XML for YouTube service, received - missing feed title!"));
00141
00142 temp = channel->getChildText(_("openSearch:totalResults"));
00143 if (temp.toInt() == 0)
00144 return subfeed;
00145
00146
00147
00148 for (int i = 0; i < channel->childCount(); i++)
00149 {
00150 Ref<Node> n = channel->getChild(i);
00151 if (n->getType() != mxml_node_element)
00152 continue;
00153
00154 Ref<Element> channel_item = RefCast(n, Element);
00155 if (channel_item->getName() != "item")
00156 continue;
00157
00158 Ref<Element> link = channel_item->getChildByName(_("gd:feedLink"));
00159 if (link == nil)
00160 continue;
00161 temp = link->getAttribute(_("href"));
00162 if (!string_ok(temp))
00163 continue;
00164
00165 subfeed->links->append(temp);
00166 }
00167
00168 return subfeed;
00169 }
00170
00171 Ref<CdsObject> YouTubeContentHandler::getNextObject()
00172 {
00173 #define DATE_BUF_LEN 12
00174 String temp;
00175 struct tm t;
00176 char datebuf[DATE_BUF_LEN];
00177 struct timespec ts;
00178
00179 while (current_node_index < channel_child_count)
00180 {
00181 Ref<Node> n = service_xml->getChild(current_node_index);
00182
00183 current_node_index++;
00184
00185 if (n == nil)
00186 return nil;
00187
00188 if (n->getType() != mxml_node_element)
00189 continue;
00190
00191 Ref<Element> channel_item = RefCast(n, Element);
00192 if (channel_item->getName() != "item")
00193 continue;
00194
00195
00196 Ref<CdsItemExternalURL> item(new CdsItemExternalURL());
00197 Ref<CdsResource> resource(new CdsResource(CH_DEFAULT));
00198 item->addResource(resource);
00199 resource->addParameter(_(ONLINE_SERVICE_AUX_ID),
00200 String::from(OS_YouTube));
00201
00202 item->setAuxData(_(ONLINE_SERVICE_AUX_ID), String::from(OS_YouTube));
00203
00204 temp = channel_item->getChildText(_("link"));
00207 if (!string_ok(temp))
00208 {
00209 log_warning("Failed to retrieve YouTube video ID\n");
00210 continue;
00211 }
00212
00213 item->setURL(temp);
00214
00215 int amp = temp.index('&');
00216 if (amp > 0 )
00217 temp = temp.substring( 0, amp );
00218
00219 int eq = temp.rindex('=');
00220 if (eq > 0)
00221 temp = temp.substring(eq + 1);
00222
00223 item->setClass(_("object.item.videoItem"));
00224 temp = String(OnlineService::getStoragePrefix(OS_YouTube)) + temp;
00225 item->setServiceID(temp);
00226
00227 temp = channel_item->getChildText(_("pubDate"));
00228 if (string_ok(temp))
00229 {
00230 datebuf[0] = '\0';
00231
00232 if (strptime(temp.c_str(), "%a, %d %b %Y %T +000", &t) != NULL)
00233 {
00234 if (strftime(datebuf, sizeof(datebuf), "%F", &t) != 0)
00235 {
00236 datebuf[DATE_BUF_LEN-1] = '\0';
00237 if (strlen(datebuf) > 0)
00238 {
00239 item->setMetadata(MetadataHandler::getMetaFieldName(M_DATE),
00240 datebuf);
00241 }
00242 }
00243 }
00244 }
00245
00246
00247 temp = channel_item->getChildText(_("author"));
00248 if (string_ok(temp))
00249 item->setAuxData(_(YOUTUBE_AUXDATA_AUTHOR), temp);
00250
00251 Ref<Element> mediagroup = channel_item->getChildByName(_("media:group"));
00252 if (mediagroup == nil)
00253 continue;
00254
00255
00256
00257
00258 bool content_set = false;
00259
00260 int mediagroup_child_count = mediagroup->elementChildCount();
00261 for (int mcc = 0; mcc < mediagroup_child_count; mcc++)
00262 {
00263 Ref<Element> el = mediagroup->getElementChild(mcc);
00264 if (el == nil)
00265 continue;
00266
00267 if (el->getName() == "media:title")
00268 {
00269 temp = el->getText();
00270 if (string_ok(temp))
00271 item->setTitle(temp);
00272 else
00273 item->setTitle(_("Unknown"));
00274 }
00275 else if (el->getName() == "media:description")
00276 {
00277 temp = el->getText();
00278 if (string_ok(temp))
00279 item->setMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION), temp);
00280 }
00281 else if (el->getName() == "media:keywords")
00282 {
00283 temp = el->getText();
00284 if (string_ok(temp))
00285 item->setAuxData(_(YOUTUBE_AUXDATA_KEYWORDS), temp);
00286 }
00287 else if (el->getName() == "media:category")
00288 {
00289 temp = el->getText();
00290 if (string_ok(temp))
00291 item->setAuxData(_(YOUTUBE_AUXDATA_CATEGORY), temp);
00292 }
00293 else if (el->getName() == "media:content")
00294 {
00295 if (content_set)
00296 continue;
00297
00298 temp = el->getAttribute(_("type"));
00299 if ((temp != YT_SWF_TYPE) && (temp != YT_MP4_TYPE))
00300 continue;
00301
00302 String mt;
00303 if (ConfigManager::getInstance()->getBoolOption(CFG_ONLINE_CONTENT_YOUTUBE_FORMAT_MP4))
00304 {
00305 mt = mp4_mimetype;
00306 }
00307 else
00308 mt = _("video/x-flv");
00309
00310 item->setMimeType(mt);
00311 resource->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO), renderProtocolInfo(mt));
00312
00313 content_set = true;
00314
00315 temp = el->getAttribute(_("duration"));
00316 if (string_ok(temp))
00317 {
00318 resource->addAttribute(MetadataHandler::getResAttrName(R_DURATION), secondsToHMS(temp.toInt()));
00319 }
00320 }
00321 else if (el->getName() == "media:thumbnail")
00322 {
00323 temp = el->getAttribute(_("url"));
00324 if (!string_ok(temp))
00325 continue;
00326
00327 if (temp.substring(temp.length()-3) != "jpg")
00328 {
00329 log_warning("Found new YouTube thumbnail image type, please report this to contact at mediatomb dot cc! [%s]\n", temp.c_str());
00330 continue;
00331 }
00332
00333 Ref<CdsResource> thumbnail(new CdsResource(CH_EXTURL));
00334 thumbnail->addOption(_(RESOURCE_CONTENT_TYPE), _(THUMBNAIL));
00335 thumbnail->addAttribute(MetadataHandler::getResAttrName(R_PROTOCOLINFO), renderProtocolInfo(thumb_mimetype));
00336 thumbnail->addOption(_(RESOURCE_OPTION_URL), temp);
00337
00338 String temp2 = el->getAttribute(_("width"));
00339 temp = el->getAttribute(_("height"));
00340
00341 if (string_ok(temp) && string_ok(temp2))
00342 {
00343 thumbnail->addAttribute(MetadataHandler::getResAttrName(R_RESOLUTION), temp2 + "x" + temp);
00344 }
00345 else
00346 continue;
00347
00348 thumbnail->addOption(_(RESOURCE_OPTION_PROXY_URL), _(FALSE));
00349 item->addResource(thumbnail);
00350 }
00351
00352 }
00353
00354 Ref<Element> stats = channel_item->getChildByName(_("yt:statistics"));
00355 if (stats != nil)
00356 {
00357 temp = stats->getAttribute(_("viewCount"));
00358 if (string_ok(temp))
00359 item->setAuxData(_(YOUTUBE_AUXDATA_VIEW_COUNT), temp);
00360
00361 temp = stats->getAttribute(_("favoriteCount"));
00362 if (string_ok(temp))
00363 item->setAuxData(_(YOUTUBE_AUXDATA_FAVORITE_COUNT), temp);
00364 }
00365
00366 Ref<Element> rating = channel_item->getChildByName(_("gd:rating"));
00367 if (rating != nil)
00368 {
00369 temp = rating->getAttribute(_("average"));
00370 if (string_ok(temp))
00371 item->setAuxData(_(YOUTUBE_AUXDATA_AVG_RATING), temp);
00372
00373 temp = rating->getAttribute(_("numRaters"));
00374 if (string_ok(temp))
00375 item->setAuxData(_(YOUTUBE_AUXDATA_RATING_COUNT), temp);
00376 }
00377
00378 item->setAuxData(_(YOUTUBE_AUXDATA_FEED), feed_name);
00379
00380 getTimespecNow(&ts);
00381 item->setAuxData(_(ONLINE_SERVICE_LAST_UPDATE), String::from(ts.tv_sec));
00382
00383 item->setFlag(OBJECT_FLAG_PROXY_URL);
00384 item->setFlag(OBJECT_FLAG_ONLINE_SERVICE);
00385 try
00386 {
00387 item->validate();
00388 return RefCast(item, CdsObject);
00389 }
00390 catch (Exception ex)
00391 {
00392 log_warning("Failed to validate newly created YouTube item: %s\n",
00393 ex.getMessage().c_str());
00394 continue;
00395 }
00396 }
00397 return nil;
00398 }
00399
00400 YouTubeSubFeed::YouTubeSubFeed()
00401 {
00402 links = Ref<Array<StringBase> >(new Array<StringBase>());
00403 title = nil;
00404 }
00405
00406 #endif//YOUTUBE