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
00031
00032 #ifdef HAVE_CONFIG_H
00033 #include "autoconfig.h"
00034 #endif
00035
00036 #ifdef YOUTUBE
00037
00038 #include "zmm/zmm.h"
00039 #include "youtube_service.h"
00040 #include "content_manager.h"
00041 #include "string_converter.h"
00042 #include "config_manager.h"
00043 #include "server.h"
00044 #include "storage.h"
00045 #include "tools.h"
00046
00047 using namespace zmm;
00048 using namespace mxml;
00049
00050
00051 #define GDATA_API_YT_BASE_URL "http://gdata.youtube.com/feeds/api/"
00052
00053
00054 #define GDATA_REQUEST_USERS "users/"
00055 #define GDATA_REQUEST_FAVORITES "/favorites"
00056 #define GDATA_REQUEST_SUBSCRIPTIONS "/subscriptions"
00057 #define GDATA_REQUEST_PLAYLISTS "/playlists"
00058 #define GDATA_REQUEST_UPLOADS "/uploads"
00059
00060 #define GDATA_REQUEST_SEARCH "videos"
00061
00062
00063 #define GDATA_REQUEST_STDFEED_BASE "standardfeeds"
00064
00065 #define GDATA_REQUEST_STDFEED_TOP_RATED "top_rated"
00066 #define GDATA_REQUEST_STDFEED_TOP_FAVORITES "top_favorites"
00067 #define GDATA_REQUEST_STDFEED_MOST_VIEWED "most_viewed"
00068 #define GDATA_REQUEST_STDFEED_MOST_RECENT "most_recent"
00069 #define GDATA_REQUEST_STDFEED_MOST_DISCUSSED "most_discussed"
00070 #define GDATA_REQUEST_STDFEED_MOST_LINKED "most_linked"
00071 #define GDATA_REQUEST_STDFEED_MOST_RESPONDED "most_responded"
00072 #define GDATA_REQUEST_STDFEED_RECENTLY_FEATURED "recently_featured"
00073 #define GDATA_REQUEST_STDFEED_WATCH_ON_MOBILE "watch_on_mobile"
00074
00075 static const char *YT_stdfeeds[] =
00076 {
00077 GDATA_REQUEST_STDFEED_TOP_RATED,
00078 GDATA_REQUEST_STDFEED_TOP_FAVORITES,
00079 GDATA_REQUEST_STDFEED_MOST_VIEWED,
00080 GDATA_REQUEST_STDFEED_MOST_RECENT,
00081 GDATA_REQUEST_STDFEED_MOST_DISCUSSED,
00082 GDATA_REQUEST_STDFEED_MOST_LINKED,
00083 GDATA_REQUEST_STDFEED_MOST_RESPONDED,
00084 GDATA_REQUEST_STDFEED_RECENTLY_FEATURED,
00085 GDATA_REQUEST_STDFEED_WATCH_ON_MOBILE,
00086 NULL,
00087 };
00088
00089
00090
00091 #define GDATA_PARAM_FEED_FORMAT "alt"
00092
00093
00094 #define GDATA_YT_PARAM_VIDEO_QUERY "vq" // value must be url escaped
00095 #define GDATA_YT_PARAM_ORDERBY "orderby"
00096 #define GDATA_YT_PARAM_FORMAT "format"
00097 #define GDATA_YT_PARAM_RESTRICT_LANGUAGE "lr"
00098 #define GDATA_YT_PARAM_RESTRICTED_CONTENT "racy"
00099 #define GDATA_YT_PARAM_COUNTRY_RESTRICTION "restriction"
00100 #define GDATA_YT_PARAM_TIME "time"
00101 #define GDATA_YT_PARAM_AUTHOR "author"
00102
00103 #define GDATA_YT_PARAM_START_INDEX "start-index"
00104 #define GDATA_YT_PARAM_MAX_RESULTS_PER_REQ "max-results"
00105 #define GDATA_YT_MAXIMUM_ALLOWED_RESULTS 1000
00106
00107
00108 #define GDATA_VALUE_FEED_FORMAT_RSS "rss"
00109
00110 #define GDATA_YT_VALUE_ORDERBY_RELEVANCE "relevance"
00111 #define GDATA_YT_VALUE_ORDERBY_PUBLISHED "published"
00112 #define GDATA_YT_VALUE_ORDERBY_VIEWCOUNT "viewCount"
00113 #define GDATA_YT_VALUE_ORDERBY_RATING "rating"
00115
00116 #define GDATA_YT_VALUE_FORMAT_SWF "5"
00118
00119
00120 #define GDATA_YT_VALUE_TIME_RANGE_ALL "all_time"
00121 #define GDATA_YT_VALUE_TIME_RANGE_DAY "today"
00122 #define GDATA_YT_VALUE_TIME_RANGE_WEEK "this_week"
00123 #define GDATA_YT_VALUE_TIME_RANGE_MONTH "this_month"
00124
00125
00126 #define GDATA_YT_VALUE_START_INDEX_MIN "1"
00127 #define GDATA_YT_VALUE_PER_PAGE_MAX "50"
00128
00129 #define GDATA_YT_VALUE_RACY_ON "include"
00130 #define GDATA_YT_VALUE_RACY_OFF "exclude"
00131
00132 #define AMOUNT_ALL (1000) // max items that we can get
00133
00134 #define CAT_NAME_FILM "Film & Animation"
00135 #define CAT_NAME_AUTOS "Autos & Vehicles"
00136 #define CAT_NAME_COMEDY "Comedy"
00137 #define CAT_NAME_ENTERTAINMENT "Entertainment"
00138 #define CAT_NAME_MUSIC "Music"
00139 #define CAT_NAME_NEWS "News & Politics"
00140 #define CAT_NAME_PEOPLE "People & Blogs"
00141 #define CAT_NAME_ANIMALS "Pets & Animals"
00142 #define CAT_NAME_HOWTO "Howto & Style"
00143 #define CAT_NAME_SPORTS "Sports"
00144 #define CAT_NAME_TRAVEL "Travel & Events"
00145 #define CAT_NAME_GADGETS "Gadgets & Games"
00146 #define CAT_NAME_SHORT_MOVIES "Short Movies"
00147 #define CAT_NAME_VIDEOBLOG "Videoblogging"
00148 #define CAT_NAME_EDUCATION "Education"
00149 #define CAT_NAME_NONPROFIT "Nonprofits & Activism"
00150
00151 #define REQ_NAME_STDFEEDS "Standard Feeds"
00152 #define REQ_NAME_VIDEO_SEARCH "Video Queries"
00153 #define REQ_NAME_FAVORITES "Favorites"
00154 #define REQ_NAME_PLAYLISTS "Playlists"
00155 #define REQ_NAME_SUBSCRIPTIONS "Subscriptions"
00156 #define REQ_NAME_UPLOADS "User Videos"
00157
00158
00159 #define CFG_CAT_TERM_FILM "Film"
00160 #define CFG_CAT_TERM_AUTOS "Autos"
00161 #define CFG_CAT_TERM_MUSIC "Music"
00162 #define CFG_CAT_TERM_ANIMALS "Animals"
00163 #define CFG_CAT_TERM_SPORTS "Sports"
00164 #define CFG_CAT_TERM_TRAVEL "Travel"
00165 #define CFG_CAT_TERM_SHORT_MOVIES "Shortmov"
00166 #define CFG_CAT_TERM_VIDEOBLOG "Videoblog"
00167 #define CFG_CAT_TERM_GADGETS "Games"
00168 #define CFG_CAT_TERM_COMEDY "Comedy"
00169 #define CFG_CAT_TERM_PEOPLE "People"
00170 #define CFG_CAT_TERM_NEWS "News"
00171 #define CFG_CAT_TERM_ENTERTAINMENT "Entertainment"
00172 #define CFG_CAT_TERM_EDUCATION "Education"
00173 #define CFG_CAT_TERM_HOWTO "Howto"
00174 #define CFG_CAT_TERM_NONPROFIT "Nonprofit"
00175 #define CFG_CAT_TERM_TECH "Tech"
00176
00177 #define CFG_REQUEST_STDFEED "standardfeed"
00178 #define CFG_REQUEST_VIDEOSEARCH "search"
00179 #define CFG_REQUEST_FAVORITES "favorites"
00180 #define CFG_REQUEST_SUBSCRIPTIONS "subscriptions"
00181 #define CFG_REQUEST_PLAYLISTS "playlists"
00182 #define CFG_REQUEST_UPLOADS "uploads"
00183
00184 #define CFG_OPTION_USER "user"
00185 #define CFG_OPTION_TAG "tag"
00186 #define CFG_OPTION_STARTINDEX "start-index"
00187 #define CFG_OPTION_AMOUNT "amount"
00188 #define CFG_OPTION_PLAYLIST_ID "id"
00189 #define CFG_OPTION_PLAYLIST_NAME "name"
00190 #define CFG_OPTION_TIME_RANGE "time-range"
00191 #define CFG_OPTION_AUTHOR "author"
00192
00193 #define CFG_OPTION_STDFEED "feed"
00194 #define CFG_OPTION_REGION_ID "region-id"
00195 #define CFG_OPTION_SEARCH_QUERY "query"
00196
00197 #define CFG_OPTION_REGION_AUSTRALIA "au"
00198 #define CFG_OPTION_REGION_BRAZIL "br"
00199 #define CFG_OPTION_REGION_CANADA "ca"
00200 #define CFG_OPTION_REGION_FRANCE "fr"
00201 #define CFG_OPTION_REGION_GERMANY "de"
00202 #define CFG_OPTION_REGION_GREAT_BRITAIN "gb"
00203 #define CFG_OPTION_REGION_HOLLAND "nl"
00204 #define CFG_OPTION_REGION_HONG_KONG "hk"
00205 #define CFG_OPTION_REGION_IRELAND "ie"
00206 #define CFG_OPTION_REGION_ITALY "it"
00207 #define CFG_OPTION_REGION_JAPAN "jp"
00208 #define CFG_OPTION_REGION_MEXICO "mx"
00209 #define CFG_OPTION_REGION_NEW_ZEALAND "nz"
00210 #define CFG_OPTION_REGION_POLAND "pl"
00211 #define CFG_OPTION_RUSSIA "ru"
00212 #define CFG_OPTION_SOUTH_KOREA "kr"
00213 #define CFG_OPTION_SPAIN "es"
00214 #define CFG_OPTION_TAIWAN "tw"
00215 #define CFG_OPTION_UNITED_STATES "us"
00216
00217 typedef struct regions regions;
00218 struct regions
00219 {
00220 yt_regions_t region;
00221 const char *region_code;
00222 const char *country;
00223 };
00224
00225 static regions YT_regions[] =
00226 {
00227 { YT_region_au, CFG_OPTION_REGION_AUSTRALIA, "Australia" },
00228 { YT_region_br, CFG_OPTION_REGION_BRAZIL, "Brazil" },
00229 { YT_region_ca, CFG_OPTION_REGION_CANADA, "Canada" },
00230 { YT_region_fr, CFG_OPTION_REGION_FRANCE, "France" },
00231 { YT_region_de, CFG_OPTION_REGION_GERMANY, "Germany" },
00232 { YT_region_gb, CFG_OPTION_REGION_GREAT_BRITAIN, "Great Britain" },
00233 { YT_region_nl, CFG_OPTION_REGION_HOLLAND, "Netherlands" },
00234 { YT_region_hk, CFG_OPTION_REGION_HONG_KONG, "Hong Kong" },
00235 { YT_region_ie, CFG_OPTION_REGION_IRELAND, "Ireland" },
00236 { YT_region_it, CFG_OPTION_REGION_ITALY, "Italy" },
00237 { YT_region_jp, CFG_OPTION_REGION_JAPAN, "Japan" },
00238 { YT_region_mx, CFG_OPTION_REGION_MEXICO, "Mexico" },
00239 { YT_region_nz, CFG_OPTION_REGION_NEW_ZEALAND, "New Zealand" },
00240 { YT_region_pl, CFG_OPTION_REGION_POLAND, "Poland" },
00241 { YT_region_ru, CFG_OPTION_RUSSIA, "Russia" },
00242 { YT_region_kr, CFG_OPTION_SOUTH_KOREA, "South Korea" },
00243 { YT_region_es, CFG_OPTION_SPAIN, "Spain" },
00244 { YT_region_tw, CFG_OPTION_TAIWAN, "Taiwan" },
00245 { YT_region_us, CFG_OPTION_UNITED_STATES, "United States" },
00246 { YT_region_none, NULL , NULL },
00247 };
00248
00249 YouTubeService::YouTubeService()
00250 {
00251 url = Ref<URL>(new URL());
00252 pid = 0;
00253 curl_handle = curl_easy_init();
00254 if (!curl_handle)
00255 throw _Exception(_("failed to initialize curl!\n"));
00256
00257 current_task = 0;
00258 }
00259
00260 YouTubeService::~YouTubeService()
00261 {
00262 if (curl_handle)
00263 curl_easy_cleanup(curl_handle);
00264 }
00265
00266 YouTubeService::YouTubeTask::YouTubeTask()
00267 {
00268 parameters = zmm::Ref<Dictionary>(new Dictionary());
00269 request = YT_request_none;
00270 region = YT_region_none;
00271 amount = 0;
00272 amount_fetched = 0;
00273 start_index = 1;
00274 cfg_start_index = 1;
00275 subfeed_index = 0;
00276 kill = false;
00277 }
00278
00279 service_type_t YouTubeService::getServiceType()
00280 {
00281 return OS_YouTube;
00282 }
00283
00284 String YouTubeService::getServiceName()
00285 {
00286 return _("YouTube");
00287 }
00288
00289 String YouTubeService::getRequestName(yt_requests_t request)
00290 {
00291 String temp;
00292
00293 switch (request)
00294 {
00295 case YT_request_stdfeed:
00296 temp = _(REQ_NAME_STDFEEDS);
00297 break;
00298 case YT_request_video_search:
00299 temp = _(REQ_NAME_VIDEO_SEARCH);
00300 break;
00301 case YT_request_user_favorites:
00302 temp = _(REQ_NAME_FAVORITES);
00303 break;
00304 case YT_request_user_playlists:
00305 case YT_subrequest_playlists:
00306 temp = _(REQ_NAME_PLAYLISTS);
00307 break;
00308 case YT_request_user_subscriptions:
00309 case YT_subrequest_subscriptions:
00310 temp = _(REQ_NAME_SUBSCRIPTIONS);
00311 break;
00312 case YT_request_user_uploads:
00313 temp = _(REQ_NAME_UPLOADS);
00314 break;
00315 case YT_request_none:
00316 default:
00317 temp = nil;
00318 break;
00319 }
00320
00321 return temp;
00322 }
00323
00324 String YouTubeService::getRegionName(yt_regions_t region_code)
00325 {
00326 if ((region_code < 0) || (region_code >= YT_region_none))
00327 return nil;
00328 else
00329 return _(YT_regions[region_code].country);
00330 }
00331
00332 #if 0
00333 String YouTubeService::getCheckAttr(Ref<Element> xml, String attrname)
00334 {
00335 String temp = xml->getAttribute(attrname);
00336 if (string_ok(temp))
00337 return temp;
00338 else
00339 throw _Exception(_("Tag <") + xml->getName() +
00340 _("> is missing the requred \"") + attrname +
00341 _("\" attribute!"));
00342 return nil;
00343 }
00344
00345 int YouTubeService::getCheckPosIntAttr(Ref<Element> xml, String attrname)
00346 {
00347 int itmp;
00348 String temp = xml->getAttribute(attrname);
00349 if (string_ok(temp))
00350 itmp = temp.toInt();
00351 else
00352 throw _Exception(_("Tag <") + xml->getName() +
00353 _("> is missing the requred \"") + attrname +
00354 _("\" attribute!"));
00355
00356 if (itmp < 1)
00357 throw _Exception(_("Invalid value in ") + attrname + _(" for <") +
00358 xml->getName() + _("> tag"));
00359
00360 return itmp;
00361 }
00362
00363 #endif
00364
00365 void YouTubeService::getPagingParams(Ref<Element> xml, Ref<YouTubeTask> task)
00366 {
00367 String temp;
00368 int itmp;
00369 temp = xml->getAttribute(_(CFG_OPTION_AMOUNT));
00370 if (!string_ok(temp))
00371 temp = _("all");
00372
00373 if (temp == "all")
00374 task->amount = AMOUNT_ALL;
00375 else
00376 {
00377 itmp = getCheckPosIntAttr(xml, _(CFG_OPTION_AMOUNT));
00378 if (itmp > AMOUNT_ALL)
00379 {
00380 log_warning("Maximum amount of items to fetch can not exceed 1000\n");
00381 itmp = AMOUNT_ALL;
00382 }
00383 task->amount = itmp;
00384 }
00385
00386 temp = xml->getAttribute(_(CFG_OPTION_STARTINDEX));
00387 if (!string_ok(temp))
00388 itmp = 1;
00389 else
00390 {
00391 itmp = getCheckPosIntAttr(xml, _(CFG_OPTION_STARTINDEX));
00392 if (itmp <= 0)
00393 {
00394 throw _Exception(_("Tag <") + xml->getName() + _("> ") +
00395 _(CFG_OPTION_STARTINDEX) + _(" must be at least 1!"));
00396 }
00397 }
00398 task->cfg_start_index = itmp;
00399 task->start_index = itmp;
00400 }
00401
00402 void YouTubeService::addTimeParams(Ref<Element> xml, Ref<YouTubeTask> task)
00403 {
00404 String temp;
00405
00406 temp = xml->getAttribute(_(CFG_OPTION_TIME_RANGE));
00407 if (!string_ok(temp))
00408 return;
00409
00410 if ((temp != GDATA_YT_VALUE_TIME_RANGE_ALL) &&
00411 (temp != GDATA_YT_VALUE_TIME_RANGE_DAY) &&
00412 (temp != GDATA_YT_VALUE_TIME_RANGE_WEEK) &&
00413 (temp != GDATA_YT_VALUE_TIME_RANGE_MONTH))
00414 throw _Exception(_("Invalid time range parameter \"") + temp +
00415 _("\" in <") + xml->getName() + _("> tag"));
00416
00417 task->parameters->put(_(GDATA_YT_PARAM_TIME), temp);
00418 }
00419
00420 yt_regions_t YouTubeService::getRegion(Ref<Element> xml)
00421 {
00422 String region = xml->getAttribute(_(CFG_OPTION_REGION_ID));
00423 if (!string_ok(region))
00424 return YT_region_none;
00425
00426 int count = 0;
00427 while (YT_regions[count].region_code != NULL)
00428 {
00429 if (region == YT_regions[count].region_code)
00430 return YT_regions[count].region;
00431 count++;
00432 }
00433
00434 throw _Exception(_("<") + xml->getName() + _("> tag has an invalid region setting: ") + region);
00435
00436 }
00437
00438 String YouTubeService::getFeed(Ref<Element> xml)
00439 {
00440 String feed = xml->getAttribute(_(CFG_OPTION_STDFEED));
00441 if (!string_ok(feed))
00442 throw _Exception(_("<") + xml->getName() + _("> tag is missing the required feed setting!"));
00443
00444 int count = 0;
00445 while (YT_stdfeeds[count] != NULL)
00446 {
00447 if (feed == YT_stdfeeds[count])
00448 return feed;
00449 count++;
00450 }
00451
00452 throw _Exception(_("<") + xml->getName() + _("> tag has an invalid feed setting: ") + feed);
00453
00454 }
00455 Ref<Object> YouTubeService::defineServiceTask(Ref<Element> xmlopt, Ref<Object> params)
00456 {
00457 Ref<YouTubeTask> task(new YouTubeTask());
00458 String temp = xmlopt->getName();
00459 String temp2;
00460 Ref<Option> racy = RefCast(params, Option);
00461
00462 if (temp == CFG_REQUEST_STDFEED)
00463 task->request = YT_request_stdfeed;
00464 else if (temp == CFG_REQUEST_VIDEOSEARCH)
00465 task->request = YT_request_video_search;
00466 else if (temp == CFG_REQUEST_FAVORITES)
00467 task->request = YT_request_user_favorites;
00468 else if (temp == CFG_REQUEST_SUBSCRIPTIONS)
00469 task->request = YT_request_user_subscriptions;
00470 else if (temp == CFG_REQUEST_PLAYLISTS)
00471 task->request = YT_request_user_playlists;
00472 else if (temp == CFG_REQUEST_UPLOADS)
00473 task->request = YT_request_user_uploads;
00474 else throw _Exception(_("Unsupported tag while parsing YouTube options: ") + temp);
00475
00476 task->amount = AMOUNT_ALL;
00477
00478 task->parameters->put(_(GDATA_PARAM_FEED_FORMAT),
00479 _(GDATA_VALUE_FEED_FORMAT_RSS));
00480 switch (task->request)
00481 {
00482 case YT_request_stdfeed:
00483 task->parameters->put(_(GDATA_YT_PARAM_FORMAT),
00484 _(GDATA_YT_VALUE_FORMAT_SWF));
00485
00486 temp2 = getFeed(xmlopt);
00487 task->url_part = _(GDATA_REQUEST_STDFEED_BASE) + _("/");
00488
00489 if (temp2 != GDATA_REQUEST_STDFEED_WATCH_ON_MOBILE)
00490 {
00491 yt_regions_t r = getRegion(xmlopt);
00492 if (r < YT_region_none)
00493 {
00494 task->url_part = task->url_part +
00495 YT_regions[r].region_code + _("/");
00496 task->region = r;
00497 }
00498 }
00499
00500 task->url_part = task->url_part + temp2;
00501 if ((temp2 != GDATA_REQUEST_STDFEED_MOST_RECENT) &&
00502 (temp2 != GDATA_REQUEST_STDFEED_RECENTLY_FEATURED) &&
00503 (temp2 != GDATA_REQUEST_STDFEED_WATCH_ON_MOBILE))
00504 {
00505 addTimeParams(xmlopt, task);
00506 }
00507
00508 task->parameters->put(_(GDATA_YT_PARAM_RESTRICTED_CONTENT),
00509 racy->getOption());
00510
00511 getPagingParams(xmlopt, task);
00512
00513 break;
00514
00515 case YT_request_video_search:
00516 task->parameters->put(_(GDATA_YT_PARAM_FORMAT),
00517 _(GDATA_YT_VALUE_FORMAT_SWF));
00518 task->url_part = _(GDATA_REQUEST_SEARCH);
00520 temp = getCheckAttr(xmlopt, _(CFG_OPTION_SEARCH_QUERY));
00521 task->parameters->put(_(GDATA_YT_PARAM_VIDEO_QUERY), temp);
00522
00523 temp = xmlopt->getAttribute(_(CFG_OPTION_AUTHOR));
00524 if (string_ok(temp))
00525 task->parameters->put(_(GDATA_YT_PARAM_AUTHOR), temp);
00526
00527 task->parameters->put(_(GDATA_YT_PARAM_RESTRICTED_CONTENT),
00528 racy->getOption());
00529
00530
00531 getPagingParams(xmlopt, task);
00532
00533 break;
00534 case YT_request_user_favorites:
00535 task->parameters->put(_(GDATA_YT_PARAM_FORMAT),
00536 _(GDATA_YT_VALUE_FORMAT_SWF));
00537 temp = getCheckAttr(xmlopt, _(CFG_OPTION_USER));
00538 task->url_part = _(GDATA_REQUEST_USERS) + temp +
00539 _(GDATA_REQUEST_FAVORITES);
00540
00541 task->parameters->put(_(GDATA_YT_PARAM_RESTRICTED_CONTENT),
00542 racy->getOption());
00543
00544 getPagingParams(xmlopt, task);
00545 break;
00546 case YT_request_user_subscriptions:
00547 temp = getCheckAttr(xmlopt, _(CFG_OPTION_USER));
00548 task->url_part = _(GDATA_REQUEST_USERS) + temp +
00549 _(GDATA_REQUEST_SUBSCRIPTIONS);
00550 task->amount = AMOUNT_ALL;
00551 break;
00552 case YT_request_user_playlists:
00553 temp = getCheckAttr(xmlopt, _(CFG_OPTION_USER));
00554 task->url_part = _(GDATA_REQUEST_USERS) + temp +
00555 _(GDATA_REQUEST_PLAYLISTS);
00556 task->amount = AMOUNT_ALL;
00557 break;
00558 case YT_request_user_uploads:
00559 task->parameters->put(_(GDATA_YT_PARAM_FORMAT),
00560 _(GDATA_YT_VALUE_FORMAT_SWF));
00561 temp = getCheckAttr(xmlopt, _(CFG_OPTION_USER));
00562 task->url_part = _(GDATA_REQUEST_USERS) + temp +
00563 _(GDATA_REQUEST_UPLOADS);
00564
00565 task->parameters->put(_(GDATA_YT_PARAM_RESTRICTED_CONTENT),
00566 racy->getOption());
00567
00568 getPagingParams(xmlopt, task);
00569 break;
00570 case YT_request_none:
00571 default:
00572 throw _Exception(_("Unsupported tag!"));
00573 break;
00574 }
00575 return RefCast(task, Object);
00576 }
00577
00578 Ref<Element> YouTubeService::getData(String url_part, Ref<Dictionary> params, bool construct_url)
00579 {
00580 long retcode;
00581 String URL;
00582 Ref<StringConverter> sc = StringConverter::i2i();
00583
00584 if (construct_url)
00585 URL = _(GDATA_API_YT_BASE_URL) + url_part;
00586 else
00587 URL = url_part;
00588
00589 if ((params != nil) && (params->size() > 0))
00590 {
00591 if (URL.index('?') > 0)
00592 URL = URL + _("&") + params->encode();
00593 else
00594 URL = URL + _("?") + params->encode();
00595 }
00596
00597 log_debug("Retrieving URL: %s\n", URL.c_str());
00598
00599 Ref<StringBuffer> buffer;
00600 try
00601 {
00602 buffer = url->download(URL, &retcode, curl_handle, false, true);
00603 }
00604 catch (Exception ex)
00605 {
00606 log_error("Failed to download YouTube XML data: %s\n",
00607 ex.getMessage().c_str());
00608 return nil;
00609 }
00610
00611 if (buffer == nil)
00612 return nil;
00613
00614 if (retcode != 200)
00615 return nil;
00616
00617
00618 Ref<Parser> parser(new Parser());
00619 try
00620 {
00621 return parser->parseString(sc->convert(buffer->toString()))->getRoot();
00622 }
00623 catch (ParseException pe)
00624 {
00625 log_error("Error parsing YouTube XML %s line %d:\n%s\n",
00626 pe.context->location.c_str(),
00627 pe.context->line,
00628 pe.getMessage().c_str());
00629 return nil;
00630 }
00631 catch (Exception ex)
00632 {
00633 log_error("Error parsing YouTube XML %s\n", ex.getMessage().c_str());
00634 return nil;
00635 }
00636
00637 return nil;
00638 }
00639
00640 void YouTubeService::killOneTimeTasks(Ref<Array<Object> > tasklist)
00641 {
00642 int current = 0;
00643
00644 for (int i = 0; i < tasklist->size(); i++)
00645 {
00646 Ref<YouTubeTask> task = RefCast(tasklist->get(i), YouTubeTask);
00647 }
00648 while (true)
00649 {
00650 Ref<YouTubeTask> task = RefCast(tasklist->get(current), YouTubeTask);
00651 if ((task != nil) && (task->kill))
00652 tasklist->removeUnordered(current);
00653 else
00654 current++;
00655
00656 if (current >= tasklist->size())
00657 break;
00658 }
00659 for (int i = 0; i < tasklist->size(); i++)
00660 {
00661 Ref<YouTubeTask> task = RefCast(tasklist->get(i), YouTubeTask);
00662 }
00663 }
00664
00665 bool YouTubeService::refreshServiceData(Ref<Layout> layout)
00666 {
00667 log_debug("Refreshing YouTube service\n");
00668
00669
00670
00671
00672
00673
00674
00675 if (pid == 0)
00676 pid = pthread_self();
00677
00678 if (pid != pthread_self())
00679 throw _Exception(_("Not allowed to call refreshServiceData from different threads!"));
00680
00681 Ref<ConfigManager> config = ConfigManager::getInstance();
00682 Ref<Array<Object> > tasklist = config->getObjectArrayOption(CFG_ONLINE_CONTENT_YOUTUBE_TASK_LIST);
00683
00684 if (tasklist->size() == 0)
00685 throw _Exception(_("Not specified what content to fetch!"));
00686
00687 Ref<YouTubeTask> task = RefCast(tasklist->get(current_task), YouTubeTask);
00688 if (task == nil)
00689 throw _Exception(_("Encountered invalid task!"));
00690
00691 if (task->amount_fetched < task->amount)
00692 {
00693
00694 task->start_index = task->cfg_start_index + task->amount_fetched;
00695 task->parameters->put(_(GDATA_YT_PARAM_START_INDEX),
00696 String::from(task->start_index));
00697
00698 if ((task->amount - task->amount_fetched) >=
00699 _(GDATA_YT_VALUE_PER_PAGE_MAX).toInt())
00700 {
00701 task->parameters->put(_(GDATA_YT_PARAM_MAX_RESULTS_PER_REQ),
00702 _(GDATA_YT_VALUE_PER_PAGE_MAX));
00703 }
00704 else if ((task->amount - task->amount_fetched) <
00705 _(GDATA_YT_VALUE_PER_PAGE_MAX).toInt())
00706 {
00707 task->parameters->put(_(GDATA_YT_PARAM_MAX_RESULTS_PER_REQ),
00708 String::from(task->amount - task->amount_fetched));
00709 }
00710 }
00711
00712
00713 bool b = false;
00714 bool construct_url = true;
00715
00716 Ref<Element> reply;
00717 Ref<YouTubeContentHandler> yt(new YouTubeContentHandler());
00718
00719 if ((task->request == YT_request_user_subscriptions) ||
00720 (task->request == YT_request_user_playlists))
00721 {
00722 reply = getData(task->url_part, task->parameters, true);
00723 if (reply == nil)
00724 throw _Exception(_("Failed to retrieve YouTube subfeed"));
00725
00726 task->subfeed = yt->getSubFeed(reply);
00727
00728
00729 for (int f = 0; f < task->subfeed->links->size(); f++)
00730 {
00731 Ref<YouTubeTask> subtask(new YouTubeTask());
00732 subtask->kill = true;
00733 if (task->request == YT_request_user_subscriptions)
00734 subtask->request = YT_subrequest_subscriptions;
00735 else if (task->request == YT_request_user_playlists)
00736 subtask->request = YT_subrequest_playlists;
00737 else
00738 subtask->request = YT_request_none;
00739
00740 subtask->url_part = task->subfeed->links->get(f);
00741 subtask->amount = AMOUNT_ALL;
00742 subtask->sub_request_name = task->subfeed->title;
00743 subtask->parameters->put(_(GDATA_YT_PARAM_FORMAT),
00744 _(GDATA_YT_VALUE_FORMAT_SWF));
00745 subtask->parameters->put(_(GDATA_PARAM_FEED_FORMAT),
00746 _(GDATA_VALUE_FEED_FORMAT_RSS));
00747
00748 tasklist->append(RefCast(subtask, Object));
00749 }
00750
00751 current_task++;
00752 if (current_task >= tasklist->size())
00753 {
00754 current_task = 0;
00755 killOneTimeTasks(tasklist);
00756 return false;
00757 }
00758 return true;
00759 }
00760
00761 if ((task->request == YT_subrequest_subscriptions) ||
00762 (task->request == YT_subrequest_playlists))
00763 construct_url = false;
00764
00765 reply = getData(task->url_part, task->parameters, construct_url);
00766
00767 if (reply != nil)
00768 b = yt->setServiceContent(reply);
00769 else
00770 {
00771 log_debug("Failed to get XML content from YouTube service\n");
00772 throw _Exception(_("Failed to get XML content from YouTube service"));
00773 }
00774
00775
00776 if (!b)
00777 {
00778 log_debug("End of pages\n");
00779 task->start_index = task->cfg_start_index;
00780 task->amount_fetched = 0;
00781
00782 current_task++;
00783 if (current_task >= tasklist->size())
00784 {
00785 current_task = 0;
00786 killOneTimeTasks(tasklist);
00787 return false;
00788 }
00789
00790 return true;
00791 }
00792
00796 Ref<CdsObject> obj;
00797 do
00798 {
00801 obj = yt->getNextObject();
00802 if (obj == nil)
00803 break;
00804
00805 obj->setVirtual(true);
00806
00807 Ref<CdsObject> old = Storage::getInstance()->loadObjectByServiceID(RefCast(obj, CdsItem)->getServiceID());
00808 if (old == nil)
00809 {
00810 log_debug("Adding new YouTube object\n");
00811 obj->setAuxData(_(YOUTUBE_AUXDATA_REQUEST),
00812 String::from(task->request));
00813
00814 if ((task->request == YT_subrequest_playlists) ||
00815 (task->request == YT_subrequest_subscriptions))
00816 {
00817 obj->setAuxData(_(YOUTUBE_AUXDATA_SUBREQUEST_NAME),
00818 task->sub_request_name);
00819 }
00820 else if (task->request == YT_request_stdfeed)
00821 {
00822 if (task->region != YT_region_none)
00823 obj->setAuxData(_(YOUTUBE_AUXDATA_REGION),
00824 String::from((int)task->region));
00825 }
00826
00827 if (layout != nil)
00828 layout->processCdsObject(obj, nil);
00829 else
00830 {
00831 log_warning("Your virtual layout is disabled, YouTube objects will not be added\n");
00832 }
00833 }
00834 else
00835 {
00836 log_debug("Updating existing YouTube object\n");
00837 obj->setID(old->getID());
00838 obj->setParentID(old->getParentID());
00839 struct timespec oldt, newt;
00840 oldt.tv_nsec = 0;
00841 oldt.tv_sec = old->getAuxData(_(ONLINE_SERVICE_LAST_UPDATE)).toLong();
00842 newt.tv_nsec = 0;
00843 newt.tv_sec = obj->getAuxData(_(ONLINE_SERVICE_LAST_UPDATE)).toLong();
00844 ContentManager::getInstance()->updateObject(obj);
00845 }
00846
00847 task->amount_fetched++;
00848
00849 if (task->amount_fetched >= task->amount)
00850 {
00851 task->amount_fetched = 0;
00852 task->start_index = task->cfg_start_index;
00853
00854 current_task++;
00855 if (current_task >= tasklist->size())
00856 {
00857 current_task = 0;
00858 killOneTimeTasks(tasklist);
00859 return false;
00860 }
00861 }
00862
00863 if (Server::getInstance()->getShutdownStatus())
00864 return false;
00865
00866 }
00867 while (obj != nil);
00868
00869 current_task++;
00870 if (current_task >= tasklist->size())
00871 {
00872 current_task = 0;
00873 killOneTimeTasks(tasklist);
00874 return false;
00875 }
00876
00877 return true;
00878 }
00879
00880 #endif//YOUTUBE