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 WEBORAMA
00037
00038 #include "zmm/zmm.h"
00039 #include "weborama_service.h"
00040 #include "weborama_content_handler.h"
00041 #include "content_manager.h"
00042 #include "string_converter.h"
00043 #include "config_manager.h"
00044 #include "config_options.h"
00045 #include "server.h"
00046
00047 using namespace zmm;
00048 using namespace mxml;
00049
00050 #define WEBORAMA_SERVICE_URL "www.weborama.ru/modules/player/index_xspf.php"
00051
00053 #define DEFAULT_PER_TASK_AMOUNT (1000)
00054
00055 #define WEBORAMA_VALUE_PER_REQUEST_MAX (100)
00056
00057 #define FILTER_OPTION_FAVORITE "favourite"
00058 #define FILTER_OPTION_FRIENDS "friends"
00059 #define FILTER_OPTION_USERID "userId"
00060 #define FILTER_OPTION_UPLOADED "uploaded"
00061
00062 #define FILTER_OPTION_USER_LAST "used_last"
00063 #define FILTER_OPTION_ALL "all"
00064 #define FILTER_OPTION_GENREID "genreId"
00065 #define FILTER_OPTION_SORT "sort"
00066 #define FILTER_SEPARATOR ";"
00067 #define FILTER_VALUE_SEPARATOR ":"
00068
00069
00070 #define PARAM_LIMIT "limit"
00071 #define PARAM_OFFSET "offset"
00072 #define PARAM_COVER_SIZE "coverSize"
00073 #define PARAM_FILTER "filter"
00074 #define PARAM_TYPE "type"
00075 #define PARAM_MOOD "mood"
00076 #define PARAM_ACTION "action"
00077 #define PARAM_ID "id"
00078
00079 #define VALUE_COVER_SIZE_DLNA "160"
00080 #define VALUE_TYPE_PLAYLIST "playlist"
00081 #define VALUE_TYPE_AUDIO "audio"
00082
00083
00084 #define CFG_REQUEST_PLAYLIST "playlist"
00085
00086 #define CFG_SECTION_FILTER "filter"
00087 #define CFG_SECTION_GENRES "genres"
00088 #define CFG_SECTION_SORT "sort"
00089 #define CFG_SECTION_TYPE "type"
00090
00091 #define CFG_SORT_FAVORITE "favorite"
00092 #define CFG_SORT_FRIENDS "friends"
00093 #define CFG_SORT_UPLOADED "uploaded"
00094 #define CFG_SORT_USED_LAST "used_last"
00095 #define CFG_SORT_ALL "all"
00096
00097 #define CFG_OPTION_PLAYLIST_NAME "name"
00098
00099 #define CFG_MOOD_0 "calm-positive"
00100 #define CFG_MOOD_1 "positive"
00101 #define CFG_MOOD_2 "energetic-positive"
00102 #define CFG_MOOD_3 "calm"
00103 #define CFG_MOOD_4 "neutral"
00104 #define CFG_MOOD_5 "energetic"
00105 #define CFG_MOOD_6 "calm-dark"
00106 #define CFG_MOOD_7 "dark"
00107 #define CFG_MOOD_8 "energetic-dark"
00108
00109 #define CFG_TYPE_AUDIO "audio"
00110 #define CFG_TYPE_PLAYLIST "playlist"
00111 #define CFG_TYPE_ALBUM "album"
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 #define CFG_GENRE_ELECT "electronic"
00134 #define CFG_GENRE_ALT "alternative"
00135 #define CFG_GENRE_JAZZ "jazz"
00136 #define CFG_GENRE_METAL "metal"
00137 #define CFG_GENRE_DMETAL "death-metal"
00138 #define CFG_GENRE_POP "pop"
00139 #define CFG_GENRE_PUNK "punk"
00140 #define CFG_GENRE_RNB "rhytm-and-blues"
00141 #define CFG_GENRE_RAP "rap"
00142 #define CFG_GENRE_REG "reggae"
00143 #define CFG_GENRE_ROCK "rock"
00144 #define CFG_GENRE_CLAS "classic"
00145 #define CFG_GENRE_ST "soundtrack"
00146 #define CFG_GENRE_RETRO "retro"
00147 #define CFG_GENRE_RUSROCK "russian-rock"
00148 #define CFG_GENRE_RUSPOP "russian-pop"
00149 #define CFG_GENRE_CHAN "chanson"
00150 #define CFG_GENRE_FOLK "folk"
00151
00152 typedef struct wr_mood wr_mood;
00153 struct wr_mood
00154 {
00155 wr_mood_t mood;
00156 const char *cfg_name;
00157 const char *aux_name;
00158 };
00159
00160 wr_mood WR_mood[] =
00161 {
00162 { WR_mood_calm_positive, CFG_MOOD_0, "Calm-Positive" },
00163 { WR_mood_positive, CFG_MOOD_1, "Positive" },
00164 { WR_mood_energetic_positive, CFG_MOOD_2, "Energetic-Positive" },
00165 { WR_mood_calm, CFG_MOOD_3, "Calm" },
00166 { WR_mood_neutral, CFG_MOOD_4, "Neutral" },
00167 { WR_mood_energetic, CFG_MOOD_5, "Energetic" },
00168 { WR_mood_calm_dark, CFG_MOOD_6, "Calm-Dark" },
00169 { WR_mood_dark, CFG_MOOD_7, "Dark" },
00170 { WR_mood_energetic_dark, CFG_MOOD_8, "Energetic-Dark" },
00171 { WR_mood_none, NULL, NULL },
00172 };
00173
00174 typedef struct wr_genre wr_genre;
00175 struct wr_genre
00176 {
00177 wr_genre_t genre;
00178 int id;
00179 const char *cfg_name;
00180 const char *aux_name;
00181 };
00182
00183 wr_genre WR_genre[] =
00184 {
00185 { WR_genre_electronic, 67, CFG_GENRE_ELECT, "Electronic" },
00186 { WR_genre_alternative, 60, CFG_GENRE_ALT, "Alternative" },
00187 { WR_genre_jazz, 65, CFG_GENRE_JAZZ, "Jazz" },
00188 { WR_genre_metal, 181, CFG_GENRE_METAL, "Metal" },
00189 { WR_genre_death_metal, 549, CFG_GENRE_DMETAL, "Death Metal" },
00190 { WR_genre_pop, 69, CFG_GENRE_POP, "Pop" },
00191 { WR_genre_punk, 388, CFG_GENRE_PUNK, "Punk" },
00192 { WR_genre_rhytm_and_blues, 738, CFG_GENRE_RNB, "Rhytm And Blues" },
00193 { WR_genre_rap, 593, CFG_GENRE_RAP, "Rap" },
00194 { WR_genre_reggae, 699, CFG_GENRE_REG, "Reggae" },
00195 { WR_genre_rock, 68, CFG_GENRE_ROCK, "Rock" },
00196 { WR_genre_classic, 976, CFG_GENRE_CLAS, "Classic" },
00197 { WR_genre_soundtrack, 353, CFG_GENRE_ST, "Soundtrack" },
00198 { WR_genre_retro, 568, CFG_GENRE_RETRO, "Retro" },
00199 { WR_genre_russian_rock, 315, CFG_GENRE_RUSROCK, "Russian Rock" },
00200 { WR_genre_russian_pop, 400, CFG_GENRE_RUSPOP, "Russian Pop" },
00201 { WR_genre_chanson, 566, CFG_GENRE_CHAN, "Chanson" },
00202 { WR_genre_folk, 63, CFG_GENRE_FOLK, "Folk" },
00203 { WR_genre_none, -1, NULL, NULL },
00204 };
00205
00206 WeboramaService::WeboramaService()
00207 {
00208 url = Ref<URL>(new URL());
00209 pid = 0;
00210 curl_handle = curl_easy_init();
00211 if (!curl_handle)
00212 throw _Exception(_("failed to initialize curl!\n"));
00213
00214 current_task = 0;
00215 }
00216
00217 WeboramaService::~WeboramaService()
00218 {
00219 if (curl_handle)
00220 curl_easy_cleanup(curl_handle);
00221 }
00222
00223 WeboramaService::WeboramaTask::WeboramaTask()
00224 {
00225 parameters = zmm::Ref<Dictionary>(new Dictionary());
00226 amount = 0;
00227 amount_fetched = 0;
00228 start_index = 0;
00229 }
00230
00231
00232 service_type_t WeboramaService::getServiceType()
00233 {
00234 return OS_Weborama;
00235 }
00236
00237 String WeboramaService::getServiceName()
00238 {
00239 return _("Weborama");
00240 }
00241
00242 wr_mood_t WeboramaService::getMood(Ref<Element> xml)
00243 {
00244 String temp = xml->getAttribute(_("mood"));
00245 if (!string_ok(temp))
00246 return WR_mood_none;
00247
00248 int i = temp.toInt();
00249 if ((i < 0) || (i >= WR_mood_none))
00250 throw _Exception(_("Weborama: invalid mood specified for tag <") +
00251 xml->getName() + ">");
00252
00253 return WR_mood[i].mood;
00254 }
00255
00256 wr_genre_t WeboramaService::getGenre(String genre)
00257 {
00258 if (!string_ok(genre))
00259 return WR_genre_none;
00260
00261 for (int i = (int)WR_genre_electronic; i < (int)WR_genre_none; i++)
00262 {
00263 if (genre == WR_genre[i].cfg_name)
00264 return WR_genre[i].genre;
00265 }
00266
00267 return WR_genre_none;
00268 }
00269
00270 int WeboramaService::getGenreID(wr_genre_t genre)
00271 {
00272 if (((int)genre < 0) || ((int)genre > WR_genre_none))
00273 return -1;
00274
00275 return WR_genre[genre].id;
00276 }
00277
00278 Ref<Object> WeboramaService::defineServiceTask(Ref<Element> xmlopt, Ref<Object> params)
00279 {
00280 Ref<WeboramaTask> task(new WeboramaTask());
00281 String temp = xmlopt->getName();
00282
00283 task->parameters->put(_(PARAM_COVER_SIZE), _(VALUE_COVER_SIZE_DLNA));
00284 task->amount = DEFAULT_PER_TASK_AMOUNT;
00285
00286 if (temp == CFG_REQUEST_PLAYLIST)
00287 {
00288 task->name = getCheckAttr(xmlopt, _(CFG_OPTION_PLAYLIST_NAME));
00289
00290 Ref<Element> type = xmlopt->getChildByName(_(CFG_SECTION_TYPE));
00291 if (type != nil)
00292 {
00293 String id = type->getAttribute(_("id"));
00294
00295 temp = type->getText();
00296 if (string_ok(temp))
00297 {
00298 if ((temp != CFG_TYPE_ALBUM) && (temp != CFG_TYPE_AUDIO) &&
00299 (temp != CFG_TYPE_PLAYLIST))
00300 throw _Exception(_("Weborama: ") + temp +
00301 _(" type is invalid!"));
00302
00303 if (((temp == CFG_TYPE_ALBUM) || (temp == CFG_TYPE_AUDIO))
00304 && (!string_ok(id) || id == "0"))
00305 {
00306 throw _Exception(_("Weborama: ") + temp +
00307 _(" type requires a valid id parameter!"));
00308 }
00309
00310 task->parameters->put(_(PARAM_TYPE), temp);
00311 if (string_ok(id))
00312 task->parameters->put(_(PARAM_ID), id);
00313 }
00314 }
00315
00316 wr_mood_t mood = getMood(xmlopt);
00317 if (mood != WR_mood_none)
00318 task->parameters->put(_(PARAM_MOOD), String::from((int)mood));
00319
00320
00321 Ref<Element> filter = xmlopt->getChildByName(_(CFG_SECTION_FILTER));
00322 if (filter != nil)
00323 {
00324 String filters;
00325 String genres = filter->getChildText(_(CFG_SECTION_GENRES));
00326 if (string_ok(genres))
00327 {
00328 String g;
00329 Ref<Array<StringBase> > parts = split_string(genres, ',');
00330 for (int i = 0; i < parts->size(); i++)
00331 {
00332 wr_genre_t genre = getGenre(parts->get(i));
00333 if (genre == WR_genre_none)
00334 continue;
00335
00336 g = g + String::from(getGenreID(genre)) + _(",");
00337 }
00338
00339 if (string_ok(g))
00340 filters = _(FILTER_OPTION_GENREID) +
00341 _(FILTER_VALUE_SEPARATOR) +
00342 g.substring(0, g.length()-1);
00343 }
00344
00345 Ref<Element> sort_tag = filter->getChildByName(_("sort"));
00346 if (sort_tag != nil)
00347 {
00348 String user_id = sort_tag->getAttribute(_("user-id"));
00349 String sort = sort_tag->getText();
00350 if (string_ok(sort))
00351 {
00352 if (!string_ok(user_id))
00353 throw _Exception(_("Weborama: sort option requires a "
00354 "valid user-id setting!"));
00355
00356 if ((sort != CFG_SORT_FAVORITE) &&
00357 (sort != CFG_SORT_FRIENDS) &&
00358 (sort != CFG_SORT_UPLOADED) &&
00359 (sort != CFG_SORT_USED_LAST) &&
00360 (sort != CFG_SORT_ALL))
00361 throw _Exception(_("Weborama: ") + sort +
00362 _(" sort is invalid!"));
00363
00364
00365
00366
00367 if (sort == CFG_SORT_FAVORITE)
00368 sort = _(FILTER_OPTION_FAVORITE);
00369
00370 if (string_ok(filters))
00371 filters = filters + _(FILTER_SEPARATOR);
00372
00373 filters = filters + _(FILTER_OPTION_SORT) +
00374 _(FILTER_VALUE_SEPARATOR) + sort +
00375 _(FILTER_SEPARATOR) +
00376 _(FILTER_OPTION_USERID) + user_id;
00377 }
00378 }
00379
00380 if (string_ok(filters))
00381 task->parameters->put(_(PARAM_FILTER), filters);
00382 }
00383
00384 temp = xmlopt->getAttribute(_("amount"));
00385 if (string_ok(temp))
00386 {
00387 int amount = temp.toInt();
00388 if (amount < 0)
00389 throw _Exception(_("Weborama: invalid amount specified for task ") + xmlopt->getName());
00390
00391 task->amount = amount;
00392 }
00393 else
00394 task->amount = DEFAULT_PER_TASK_AMOUNT;
00395 }
00396 else
00397 return nil;
00398
00399 return RefCast(task, Object);
00400 }
00401
00402 Ref<Element> WeboramaService::getData(Ref<Dictionary> params)
00403 {
00404 long retcode;
00405 Ref<StringConverter> sc = StringConverter::i2i();
00406
00407 Ref<StringBuffer> buffer;
00408
00409 try
00410 {
00411 String URL = _(WEBORAMA_SERVICE_URL) + _("?") + params->encode();
00412 log_debug("DOWNLOADING URL: %s\n", URL.c_str());
00413 buffer = url->download(URL, &retcode,
00414 curl_handle, false, true, true);
00415 }
00416 catch (Exception ex)
00417 {
00418 log_error("Failed to download Weborama XML data: %s\n",
00419 ex.getMessage().c_str());
00420 return nil;
00421 }
00422
00423 if (buffer == nil)
00424 return nil;
00425
00426 if (retcode != 200)
00427 return nil;
00428
00429 log_debug("GOT BUFFER\n%s\n", buffer->toString().c_str());
00430 Ref<Parser> parser(new Parser());
00431 try
00432 {
00433 return parser->parseString(sc->convert(buffer->toString()))->getRoot();
00434 }
00435 catch (ParseException pe)
00436 {
00437 log_error("Error parsing Weborama XML %s line %d:\n%s\n",
00438 pe.context->location.c_str(),
00439 pe.context->line,
00440 pe.getMessage().c_str());
00441 return nil;
00442 }
00443 catch (Exception ex)
00444 {
00445 log_error("Error parsing Weborama XML %s\n", ex.getMessage().c_str());
00446 return nil;
00447 }
00448
00449 return nil;
00450 }
00451
00452 bool WeboramaService::refreshServiceData(Ref<Layout> layout)
00453 {
00454 log_debug("Refreshing Weborama service\n");
00455
00456
00457
00458
00459
00460
00461
00462 if (pid == 0)
00463 pid = pthread_self();
00464
00465 if (pid != pthread_self())
00466 throw _Exception(_("Not allowed to call refreshServiceData from different threads!"));
00467
00468 Ref<ConfigManager> config = ConfigManager::getInstance();
00469 Ref<Array<Object> > tasklist = config->getObjectArrayOption(CFG_ONLINE_CONTENT_WEBORAMA_TASK_LIST);
00470
00471 if (tasklist->size() == 0)
00472 throw _Exception(_("Not specified what content to fetch!"));
00473
00474 Ref<WeboramaTask> task = RefCast(tasklist->get(current_task), WeboramaTask);
00475 if (task == nil)
00476 throw _Exception(_("Encountered invalid task!"));
00477
00478 if (task->amount_fetched < task->amount)
00479 {
00480 task->start_index = task->amount_fetched;
00481 task->parameters->put(_(PARAM_OFFSET), String::from(task->start_index));
00482 if ((task->amount - task->amount_fetched) >=
00483 WEBORAMA_VALUE_PER_REQUEST_MAX)
00484 {
00485 task->parameters->put(_(PARAM_LIMIT),
00486 String::from(WEBORAMA_VALUE_PER_REQUEST_MAX));
00487 }
00488 else if ((task->amount - task->amount_fetched) <
00489 WEBORAMA_VALUE_PER_REQUEST_MAX)
00490 {
00491 task->parameters->put(_(PARAM_LIMIT),
00492 String::from(task->amount - task->amount_fetched));
00493 }
00494 }
00495 bool b = false;
00496
00497 Ref<Element> reply = getData(task->parameters);
00498
00499 Ref<WeboramaContentHandler> sc(new WeboramaContentHandler());
00500 if (reply != nil)
00501 b = sc->setServiceContent(reply);
00502 else
00503 {
00504 log_debug("Failed to get XML content from Weborama service\n");
00505 throw _Exception(_("Failed to get XML content from Weborama service"));
00506 }
00507
00508 if (!b)
00509 {
00510 log_debug("End of pages\n");
00511 task->amount_fetched = 0;
00512 task->start_index = 0;
00513
00514 current_task++;
00515 if (current_task >= tasklist->size())
00516 {
00517 current_task = 0;
00518 return false;
00519 }
00520
00521 return true;
00522 }
00523
00527 Ref<CdsObject> obj;
00528 do
00529 {
00530 obj = sc->getNextObject();
00531 if (obj == nil)
00532 {
00533 if (task->amount_fetched < task->amount)
00534 return true;
00535 else
00536 break;
00537 }
00538
00539 obj->setVirtual(true);
00540 obj->setAuxData(_(WEBORAMA_AUXDATA_REQUEST_NAME), task->name);
00541 RefCast(obj,
00542 CdsItemExternalURL)->setTrackNumber(task->amount_fetched+1);
00543
00544 Ref<CdsObject> old = Storage::getInstance()->loadObjectByServiceID(RefCast(obj, CdsItem)->getServiceID());
00545 if (old == nil)
00546 {
00547 log_debug("Adding new Weborama object\n");
00548
00549 if (layout != nil)
00550 layout->processCdsObject(obj, nil);
00551 }
00552 else
00553 {
00554 log_debug("Updating existing Weborama object\n");
00555 obj->setID(old->getID());
00556 obj->setParentID(old->getParentID());
00557 struct timespec oldt, newt;
00558 oldt.tv_nsec = 0;
00559 oldt.tv_sec = old->getAuxData(_(ONLINE_SERVICE_LAST_UPDATE)).toLong();
00560 newt.tv_nsec = 0;
00561 newt.tv_sec = obj->getAuxData(_(ONLINE_SERVICE_LAST_UPDATE)).toLong();
00562 ContentManager::getInstance()->updateObject(obj);
00563 }
00564
00565 task->amount_fetched++;
00566 if (task->amount_fetched >= task->amount)
00567 {
00568 task->amount_fetched = 0;
00569 task->start_index = 0;
00570
00571 current_task++;
00572 if (current_task >= tasklist->size())
00573 {
00574 current_task = 0;
00575 return false;
00576 }
00577 }
00578
00579 if (Server::getInstance()->getShutdownStatus())
00580 return false;
00581
00582 }
00583 while (obj != nil);
00584
00585 current_task++;
00586 if (current_task >= tasklist->size())
00587 {
00588 current_task = 0;
00589 return false;
00590 }
00591
00592 return false;
00593 }
00594
00595 #endif//WEBORAMA