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 #include <sys/types.h>
00037 #include <sys/stat.h>
00038 #include <dirent.h>
00039 #include <errno.h>
00040 #include <string.h>
00041 #include <unistd.h>
00042
00043 #include "tools.h"
00044 #include "rexp.h"
00045 #include "content_manager.h"
00046 #include "config_manager.h"
00047 #include "update_manager.h"
00048 #include "string_converter.h"
00049 #include "metadata_handler.h"
00050 #include "session_manager.h"
00051 #include "timer.h"
00052 #include "layout/fallback_layout.h"
00053 #include "filesystem.h"
00054
00055 #ifdef HAVE_JS
00056 #include "layout/js_layout.h"
00057 #endif
00058
00059 #ifdef EXTERNAL_TRANSCODING
00060 #include "process.h"
00061 #endif
00062
00063 #ifdef YOUTUBE
00064 #include "youtube_service.h"
00065 #include <time.h>
00066 #endif
00067
00068 #ifdef SOPCAST
00069 #include "sopcast_service.h"
00070 #endif
00071
00072 #ifdef WEBORAMA
00073 #include "weborama_service.h"
00074 #endif
00075
00076 #ifdef ATRAILERS
00077 #include "atrailers_service.h"
00078 #endif
00079
00080 #ifdef ONLINE_SERVICES
00081 #include "task_processor.h"
00082 #endif
00083
00084 #define DEFAULT_DIR_CACHE_CAPACITY 10
00085 #define CM_INITIAL_QUEUE_SIZE 20
00086
00087 #ifdef HAVE_MAGIC
00088
00089 extern "C" {
00090 #include <magic.h>
00091 }
00092
00093 struct magic_set *ms = NULL;
00094 #endif
00095
00096 using namespace zmm;
00097 using namespace mxml;
00098
00099 #define MIMETYPE_REGEXP "^([a-z0-9_-]+/[a-z0-9_-]+)"
00100
00101 SINGLETON_MUTEX(ContentManager, true);
00102
00103 static String get_filename(String path)
00104 {
00105 if (path.charAt(path.length() - 1) == DIR_SEPARATOR)
00106 path = path.substring(0, path.length() - 1);
00107 int pos = path.rindex(DIR_SEPARATOR);
00108 if (pos < 0)
00109 return path;
00110 else
00111 return path.substring(pos + 1);
00112 }
00113
00114 ContentManager::ContentManager() : TimerSubscriberSingleton<ContentManager>()
00115 {
00116 int i;
00117 cond = Ref<Cond>(new Cond(mutex));
00118 ignore_unknown_extensions = false;
00119 extension_map_case_sensitive = false;
00120
00121 taskID = 1;
00122 working = false;
00123 shutdownFlag = false;
00124 layout_enabled = false;
00125
00126 acct = Ref<CMAccounting>(new CMAccounting());
00127 taskQueue1 = Ref<ObjectQueue<GenericTask> >(new ObjectQueue<GenericTask>(CM_INITIAL_QUEUE_SIZE));
00128 taskQueue2 = Ref<ObjectQueue<GenericTask> >(new ObjectQueue<GenericTask>(CM_INITIAL_QUEUE_SIZE));
00129
00130 Ref<ConfigManager> cm = ConfigManager::getInstance();
00131 Ref<Element> tmpEl;
00132
00133
00134
00135 extension_mimetype_map =
00136 cm->getDictionaryOption(CFG_IMPORT_MAPPINGS_EXTENSION_TO_MIMETYPE_LIST);
00137
00138 ignore_unknown_extensions = cm->getBoolOption(CFG_IMPORT_MAPPINGS_IGNORE_UNKNOWN_EXTENSIONS);
00139
00140 if (ignore_unknown_extensions && (extension_mimetype_map->size() == 0))
00141 {
00142 log_warning("Ignore unknown extensions set, but no mappings specified\n");
00143 log_warning("Please review your configuration!\n");
00144 ignore_unknown_extensions = false;
00145 }
00146
00147 extension_map_case_sensitive = cm->getBoolOption(CFG_IMPORT_MAPPINGS_EXTENSION_TO_MIMETYPE_CASE_SENSITIVE);
00148
00149 mimetype_upnpclass_map =
00150 cm->getDictionaryOption(CFG_IMPORT_MAPPINGS_MIMETYPE_TO_UPNP_CLASS_LIST);
00151
00152 mimetype_contenttype_map =
00153 cm->getDictionaryOption(CFG_IMPORT_MAPPINGS_MIMETYPE_TO_CONTENTTYPE_LIST);
00154 Ref<AutoscanList> config_timed_list =
00155 cm->getAutoscanListOption(CFG_IMPORT_AUTOSCAN_TIMED_LIST);
00156
00157 for (i = 0; i < config_timed_list->size(); i++)
00158 {
00159 Ref<AutoscanDirectory> dir = config_timed_list->get(i);
00160 if (dir != nil)
00161 {
00162 String path = dir->getLocation();
00163 if (check_path(path, true))
00164 {
00165 dir->setObjectID(ensurePathExistence(path));
00166 }
00167 }
00168 }
00169
00170 Ref<Storage> storage = Storage::getInstance();
00171 storage->updateAutoscanPersistentList(TimedScanMode, config_timed_list);
00172 autoscan_timed = storage->getAutoscanList(TimedScanMode);
00173
00174 #ifdef HAVE_INOTIFY
00175 if (cm->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
00176 {
00177 Ref<AutoscanList> config_inotify_list =
00178 cm->getAutoscanListOption(CFG_IMPORT_AUTOSCAN_INOTIFY_LIST);
00179
00180 for (i = 0; i < config_inotify_list->size(); i++)
00181 {
00182 Ref<AutoscanDirectory> dir = config_inotify_list->get(i);
00183 if (dir != nil)
00184 {
00185 String path = dir->getLocation();
00186 if (check_path(path, true))
00187 {
00188 dir->setObjectID(ensurePathExistence(path));
00189 }
00190 }
00191 }
00192
00193 storage->updateAutoscanPersistentList(InotifyScanMode,
00194 config_inotify_list);
00195 autoscan_inotify = storage->getAutoscanList(InotifyScanMode);
00196 }
00197 else
00198
00199 autoscan_inotify = Ref<AutoscanList>(new AutoscanList());
00200 #endif
00201
00202 #ifdef HAVE_MAGIC
00203 if (! ignore_unknown_extensions)
00204 {
00205 ms = magic_open(MAGIC_MIME);
00206 if (ms == NULL)
00207 {
00208 log_error("magic_open failed\n");
00209 }
00210 else
00211 {
00212 String magicFile = cm->getOption(CFG_IMPORT_MAGIC_FILE);
00213 if (! string_ok(magicFile))
00214 magicFile = nil;
00215 if (magic_load(ms, (magicFile == nil) ? NULL : magicFile.c_str()) == -1)
00216 {
00217 log_warning("magic_load: %s\n", magic_error(ms));
00218 magic_close(ms);
00219 ms = NULL;
00220 }
00221 }
00222 }
00223 #endif // HAVE_MAGIC
00224
00225 String layout_type =
00226 cm->getOption(CFG_IMPORT_SCRIPTING_VIRTUAL_LAYOUT_TYPE);
00227 if ((layout_type == "builtin") || (layout_type == "js"))
00228 layout_enabled = true;
00229
00230 #ifdef ONLINE_SERVICES
00231 online_services = Ref<OnlineServiceList>(new OnlineServiceList());
00232 #ifdef YOUTUBE
00233 if (cm->getBoolOption(CFG_ONLINE_CONTENT_YOUTUBE_ENABLED))
00234 {
00235 try
00236 {
00237 Ref<OnlineService> yt((OnlineService *)new YouTubeService());
00238
00239 i = cm->getIntOption(CFG_ONLINE_CONTENT_YOUTUBE_REFRESH);
00240 yt->setRefreshInterval(i);
00241
00242 i = cm->getIntOption(CFG_ONLINE_CONTENT_YOUTUBE_PURGE_AFTER);
00243 yt->setItemPurgeInterval(i);
00244
00245 if (cm->getBoolOption(CFG_ONLINE_CONTENT_YOUTUBE_UPDATE_AT_START))
00246 i = CFG_DEFAULT_UPDATE_AT_START;
00247
00248 Ref<TimerParameter> yt_param(new TimerParameter(TimerParameter::IDOnlineContent, OS_YouTube));
00249 yt->setTimerParameter(RefCast(yt_param, Object));
00250 online_services->registerService(yt);
00251
00252 if (i > 0)
00253 {
00254 Timer::getInstance()->addTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON(this), i, yt->getTimerParameter(), true);
00255 }
00256 }
00257 catch (Exception ex)
00258 {
00259 log_error("Could not setup YouTube: %s\n",
00260 ex.getMessage().c_str());
00261 }
00262 }
00263
00264
00265 cached_urls = Ref<ReentrantArray<CachedURL> >
00266 (new ReentrantArray<CachedURL>(MAX_CACHED_URLS));
00267 urlcache_mutex = Ref<Mutex>(new Mutex(false));
00268
00269 #endif //YOUTUBE
00270
00271 #ifdef SOPCAST
00272 if (cm->getBoolOption(CFG_ONLINE_CONTENT_SOPCAST_ENABLED))
00273 {
00274 try
00275 {
00276 Ref<OnlineService> sc((OnlineService *)new SopCastService());
00277
00278 i = cm->getIntOption(CFG_ONLINE_CONTENT_SOPCAST_REFRESH);
00279 sc->setRefreshInterval(i);
00280
00281 i = cm->getIntOption(CFG_ONLINE_CONTENT_SOPCAST_PURGE_AFTER);
00282 sc->setItemPurgeInterval(i);
00283
00284 if (cm->getBoolOption(CFG_ONLINE_CONTENT_SOPCAST_UPDATE_AT_START))
00285 i = CFG_DEFAULT_UPDATE_AT_START;
00286
00287 Ref<TimerParameter> sc_param(new TimerParameter(TimerParameter::IDOnlineContent, OS_SopCast));
00288 sc->setTimerParameter(RefCast(sc_param, Object));
00289 online_services->registerService(sc);
00290 if (i > 0)
00291 {
00292 Timer::getInstance()->addTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON(this), i, sc->getTimerParameter(), true);
00293 }
00294 }
00295 catch (Exception ex)
00296 {
00297 log_error("Could not setup SopCast: %s\n",
00298 ex.getMessage().c_str());
00299 }
00300 }
00301 #endif //SOPCAST
00302
00303 #ifdef WEBORAMA
00304 if (cm->getBoolOption(CFG_ONLINE_CONTENT_WEBORAMA_ENABLED))
00305 {
00306 try
00307 {
00308 Ref<OnlineService> wb((OnlineService *)new WeboramaService());
00309 i = cm->getIntOption(CFG_ONLINE_CONTENT_WEBORAMA_REFRESH);
00310 wb->setRefreshInterval(i);
00311
00312 i = cm->getIntOption(CFG_ONLINE_CONTENT_WEBORAMA_PURGE_AFTER);
00313 wb->setItemPurgeInterval(i);
00314
00315 if (cm->getBoolOption(CFG_ONLINE_CONTENT_WEBORAMA_UPDATE_AT_START))
00316 i = CFG_DEFAULT_UPDATE_AT_START;
00317
00318 Ref<TimerParameter> wb_param(new TimerParameter(TimerParameter::IDOnlineContent, OS_Weborama));
00319 wb->setTimerParameter(RefCast(wb_param, Object));
00320 online_services->registerService(wb);
00321 if (i > 0)
00322 {
00323 Timer::getInstance()->addTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON(this), i, wb->getTimerParameter(), true);
00324 }
00325 }
00326 catch (Exception ex)
00327 {
00328 log_error("Could not setup Weborama: %s\n",
00329 ex.getMessage().c_str());
00330 }
00331 }
00332 #endif //WEBORAMA
00333
00334 #ifdef ATRAILERS
00335 if (cm->getBoolOption(CFG_ONLINE_CONTENT_ATRAILERS_ENABLED))
00336 {
00337 try
00338 {
00339 Ref<OnlineService> at((OnlineService *)new ATrailersService());
00340 i = cm->getIntOption(CFG_ONLINE_CONTENT_ATRAILERS_REFRESH);
00341 at->setRefreshInterval(i);
00342
00343 i = cm->getIntOption(CFG_ONLINE_CONTENT_ATRAILERS_PURGE_AFTER);
00344 at->setItemPurgeInterval(i);
00345 if (cm->getBoolOption(CFG_ONLINE_CONTENT_ATRAILERS_UPDATE_AT_START))
00346 i = CFG_DEFAULT_UPDATE_AT_START;
00347
00348 Ref<TimerParameter> at_param(new TimerParameter(TimerParameter::IDOnlineContent, OS_ATrailers));
00349 at->setTimerParameter(RefCast(at_param, Object));
00350 online_services->registerService(at);
00351 if (i > 0)
00352 {
00353 Timer::getInstance()->addTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON(this), i, at->getTimerParameter(), true);
00354 }
00355 }
00356 catch (Exception ex)
00357 {
00358 log_error("Could not setup Apple Trailers: %s\n",
00359 ex.getMessage().c_str());
00360 }
00361 }
00362 #endif//ATRAILERS
00363
00364 #endif //ONLINE_SERVICES
00365 }
00366
00367 ContentManager::~ContentManager()
00368 {
00369 log_debug("ContentManager destroyed\n");
00370 }
00371
00372 void ContentManager::init()
00373 {
00374 int ret;
00375
00376 reMimetype = Ref<RExp>(new RExp());
00377 reMimetype->compile(_(MIMETYPE_REGEXP));
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390 ret = pthread_create(
00391 &taskThread,
00392 NULL,
00393 ContentManager::staticThreadProc,
00394 this
00395 );
00396 if (ret != 0)
00397 {
00398 throw _Exception(_("Could not start task thread"));
00399 }
00400
00401
00402
00403
00404
00405 autoscan_timed->notifyAll(AS_TIMER_SUBSCRIBER_SINGLETON(this));
00406
00407 #ifdef HAVE_INOTIFY
00408 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
00409 {
00410 inotify = Ref<AutoscanInotify>(new AutoscanInotify());
00412 for (int i = 0; i < autoscan_inotify->size(); i++)
00413 {
00414 Ref<AutoscanDirectory> dir = autoscan_inotify->get(i);
00415 if (dir != nil)
00416 inotify->monitor(dir);
00417 }
00418 }
00419 #endif
00420
00421 #ifdef EXTERNAL_TRANSCODING
00422 process_list = Ref<Array<Executor> >(new Array<Executor>());
00423 #endif
00424 }
00425
00426 #if defined(EXTERNAL_TRANSCODING) || defined(SOPCAST)
00427 void ContentManager::registerExecutor(Ref<Executor> exec)
00428 {
00429 AUTOLOCK(mutex);
00430 process_list->append(exec);
00431 }
00432
00433 void ContentManager::unregisterExecutor(Ref<Executor> exec)
00434 {
00435
00436
00437
00438
00439
00440
00441 if (shutdownFlag)
00442 return;
00443
00444 AUTOLOCK(mutex);
00445 for (int i = 0; i < process_list->size(); i++)
00446 {
00447 if (process_list->get(i) == exec)
00448 process_list->remove(i);
00449 }
00450 }
00451 #endif
00452
00453 void ContentManager::timerNotify(Ref<Object> parameter)
00454 {
00455 if (parameter == nil)
00456 return;
00457
00458 Ref<TimerParameter> tp = RefCast(parameter, TimerParameter);
00459 if (tp->whoami() == TimerParameter::IDAutoscan)
00460 {
00461 Ref<AutoscanDirectory> dir = autoscan_timed->get(tp->getID());
00462 if (dir == nil)
00463 return;
00464
00465 int objectID = dir->getObjectID();
00466 rescanDirectory(objectID, dir->getScanID(), dir->getScanMode());
00467 }
00468 #ifdef ONLINE_SERVICES
00469 else if (tp->whoami() == TimerParameter::IDOnlineContent)
00470 {
00471 fetchOnlineContent((service_type_t)(tp->getID()));
00472 }
00473 #ifdef YOUTUBE
00474 else if (tp->whoami() == TimerParameter::IDURLCache)
00475 {
00476 checkCachedURLs();
00477 }
00478 #endif
00479 #endif//ONLINE_SERVICES
00480
00481 }
00482
00483 void ContentManager::shutdown()
00484 {
00485 #ifdef HAVE_INOTIFY
00486 inotify = nil;
00487 #endif
00488 log_debug("start\n");
00489 AUTOLOCK(mutex);
00490 log_debug("updating last_modified data for autoscan in database...\n");
00491 autoscan_timed->updateLMinDB();
00492
00493 #ifdef HAVE_INOTIFY
00494 for (int i = 0; i < autoscan_inotify->size(); i++)
00495 {
00496 Ref<AutoscanDirectory> dir = autoscan_inotify->get(i);
00497 if (dir != nil)
00498 {
00499 try
00500 {
00501 dir->resetLMT();
00502 dir->setCurrentLMT(check_path_ex(dir->getLocation(), true));
00503 dir->updateLMT();
00504 }
00505 catch (Exception ex)
00506 {
00507 continue;
00508 }
00509 }
00510 }
00511
00512 autoscan_inotify->updateLMinDB();
00513 #endif
00514
00515 shutdownFlag = true;
00516
00517 #ifdef EXTERNAL_TRANSCODING
00518 for (int i = 0; i < process_list->size(); i++)
00519 {
00520 Ref<Executor> exec = process_list->get(i);
00521 if (exec != nil)
00522 exec->kill();
00523 }
00524 #endif
00525
00526 log_debug("signalling...\n");
00527 signal();
00528 AUTOUNLOCK();
00529 log_debug("waiting for thread...\n");
00530
00531 if (taskThread)
00532 pthread_join(taskThread, NULL);
00533 taskThread = 0;
00534
00535 #ifdef HAVE_MAGIC
00536 if (ms)
00537 {
00538 magic_close(ms);
00539 ms = NULL;
00540 }
00541 #endif
00542 log_debug("end\n");
00543 }
00544
00545 Ref<CMAccounting> ContentManager::getAccounting()
00546 {
00547 return acct;
00548 }
00549 Ref<GenericTask> ContentManager::getCurrentTask()
00550 {
00551 Ref<GenericTask> task;
00552 AUTOLOCK(mutex);
00553 task = currentTask;
00554 return task;
00555 }
00556
00557 Ref<Array<GenericTask> > ContentManager::getTasklist()
00558 {
00559 int i;
00560
00561 AUTOLOCK(mutex);
00562
00563
00564 Ref<Array<GenericTask> > taskList = nil;
00565 #ifdef ONLINE_SERVICES
00566 taskList = TaskProcessor::getInstance()->getTasklist();
00567 #endif
00568 Ref<GenericTask> t = getCurrentTask();
00569
00570
00571
00572 if ((t == nil) && (taskList == nil))
00573 return nil;
00574
00575 if (taskList == nil)
00576 taskList = Ref<Array<GenericTask> >(new Array<GenericTask>());
00577
00578 if (t != nil)
00579 taskList->append(t);
00580
00581 int qsize = taskQueue1->size();
00582
00583 for (i = 0; i < qsize; i++)
00584 {
00585 Ref<GenericTask> t = taskQueue1->get(i);
00586 if (t->isValid())
00587 taskList->append(t);
00588 }
00589
00590 qsize = taskQueue2->size();
00591 for (i = 0; i < qsize; i++)
00592 {
00593 Ref<GenericTask> t = taskQueue2->get(i);
00594 if (t->isValid())
00595 taskList = Ref<Array<GenericTask> >(new Array<GenericTask>());
00596 }
00597
00598 return taskList;
00599 }
00600
00601
00602 void ContentManager::_loadAccounting()
00603 {
00604 Ref<Storage> storage = Storage::getInstance();
00605 acct->totalFiles = storage->getTotalFiles();
00606 }
00607
00608 void ContentManager::addVirtualItem(Ref<CdsObject> obj, bool allow_fifo)
00609 {
00610 obj->validate();
00611 String path = obj->getLocation();
00612 check_path_ex(path, false, false);
00613 Ref<Storage> storage = Storage::getInstance();
00614 Ref<CdsObject> pcdir = storage->findObjectByPath(path);
00615 if (pcdir == nil)
00616 {
00617 pcdir = createObjectFromFile(path, true, allow_fifo);
00618 if (pcdir == nil)
00619 {
00620 throw _Exception(_("Could not add ") + path);
00621 }
00622 if (IS_CDS_ITEM(pcdir->getObjectType()))
00623 {
00624 this->addObject(pcdir);
00625 obj->setRefID(pcdir->getID());
00626 }
00627 }
00628
00629 addObject(obj);
00630
00631 }
00632
00633 int ContentManager::_addFile(String path, String rootpath, bool recursive, bool hidden, Ref<GenericTask> task)
00634 {
00635 if (hidden == false)
00636 {
00637 String filename = get_filename(path);
00638 if (string_ok(filename) && filename.charAt(0) == '.')
00639 return INVALID_OBJECT_ID;
00640 }
00641
00642
00643 if (ConfigManager::getInstance()->getConfigFilename() == path)
00644 return INVALID_OBJECT_ID;
00645
00646 if (layout_enabled)
00647 initLayout();
00648
00649 #ifdef HAVE_JS
00650 initJS();
00651 #endif
00652
00653 Ref<Storage> storage = Storage::getInstance();
00654
00655 Ref<UpdateManager> um = UpdateManager::getInstance();
00656
00657
00658 Ref<CdsObject> obj = storage->findObjectByPath(path);
00659 if (obj == nil)
00660 {
00661 obj = createObjectFromFile(path);
00662 if (obj == nil)
00663 return INVALID_OBJECT_ID;
00664 if (IS_CDS_ITEM(obj->getObjectType()))
00665 {
00666 addObject(obj);
00667 if (layout != nil)
00668 {
00669 try
00670 {
00671 if (!string_ok(rootpath) && (task != nil))
00672 rootpath = RefCast(task, CMAddFileTask)->getRootPath();
00673
00674 layout->processCdsObject(obj, rootpath);
00675
00676 String mimetype = RefCast(obj, CdsItem)->getMimeType();
00677 String content_type = mimetype_contenttype_map->get(mimetype);
00678 #ifdef HAVE_JS
00679 if ((playlist_parser_script != nil) &&
00680 (content_type == CONTENT_TYPE_PLAYLIST))
00681 playlist_parser_script->processPlaylistObject(obj, task);
00682 #ifdef HAVE_LIBDVDNAV
00683 if ((dvd_import_script != nil) &&
00684 (content_type == CONTENT_TYPE_DVD))
00685 dvd_import_script->processDVDObject(obj);
00686 #else
00687 if (content_type == CONTENT_TYPE_DVD)
00688 log_warning("DVD Image %s will not be parsed: MediaTomb was compiled without libdvdnav support!\n", obj->getLocation().c_str());
00689 #endif // DVD
00690 #else
00691 if (content_type == CONTENT_TYPE_PLAYLIST)
00692 log_warning("Playlist %s will not be parsed: MediaTomb was compiled without JS support!\n", obj->getLocation().c_str());
00693 #endif // JS
00694 }
00695 catch (Exception e)
00696 {
00697 throw e;
00698 }
00699 }
00700 }
00701 }
00702
00703 if (recursive && IS_CDS_CONTAINER(obj->getObjectType()))
00704 {
00705 addRecursive(path, hidden, task);
00706 }
00707 return obj->getID();
00708 }
00709
00710 void ContentManager::_removeObject(int objectID, bool all)
00711 {
00712 if (objectID == CDS_ID_ROOT)
00713 throw _Exception(_("cannot remove root container"));
00714 if (objectID == CDS_ID_FS_ROOT)
00715 throw _Exception(_("cannot remove PC-Directory container"));
00716 if (IS_FORBIDDEN_CDS_ID(objectID))
00717 throw _Exception(_("tried to remove illegal object id"));
00718
00719 Ref<Storage> storage = Storage::getInstance();
00720
00721 Ref<Storage::ChangedContainers> changedContainers = storage->removeObject(objectID, all);
00722
00723 if (changedContainers != nil)
00724 {
00725 SessionManager::getInstance()->containerChangedUI(changedContainers->ui);
00726 UpdateManager::getInstance()->containersChanged(changedContainers->upnp);
00727 }
00728
00729
00730
00731 }
00732
00733 int ContentManager::ensurePathExistence(zmm::String path)
00734 {
00735 int updateID;
00736 int containerID = Storage::getInstance()->ensurePathExistence(path, &updateID);
00737 if (updateID != INVALID_OBJECT_ID)
00738 {
00739 UpdateManager::getInstance()->containerChanged(updateID);
00740 SessionManager::getInstance()->containerChangedUI(updateID);
00741 }
00742 return containerID;
00743 }
00744
00745 void ContentManager::_rescanDirectory(int containerID, int scanID, scan_mode_t scanMode, scan_level_t scanLevel, Ref<GenericTask> task)
00746 {
00747 log_debug("start\n");
00748 int ret;
00749 struct dirent *dent;
00750 struct stat statbuf;
00751 String location;
00752 String path;
00753 Ref<CdsObject> obj;
00754
00755 if (scanID == INVALID_SCAN_ID)
00756 return;
00757
00758 Ref<AutoscanDirectory> adir = getAutoscanDirectory(scanID, scanMode);
00759 if (adir == nil)
00760 throw _Exception(_("ID valid but nil returned? this should never happen"));
00761
00762 Ref<Storage> storage = Storage::getInstance();
00763
00764 if (containerID != INVALID_OBJECT_ID)
00765 {
00766 try
00767 {
00768 obj = storage->loadObject(containerID);
00769 if (!IS_CDS_CONTAINER(obj->getObjectType()))
00770 {
00771 throw _Exception(_("Not a container"));
00772 }
00773 if (containerID == CDS_ID_FS_ROOT)
00774 location = _(FS_ROOT_DIRECTORY);
00775 else
00776 location = obj->getLocation();
00777 }
00778 catch (Exception e)
00779 {
00780 if (adir->persistent())
00781 {
00782 containerID = INVALID_OBJECT_ID;
00783 }
00784 else
00785 {
00786 adir->setTaskCount(-1);
00787 removeAutoscanDirectory(scanID, scanMode);
00788 storage->removeAutoscanDirectory(adir->getStorageID());
00789 return;
00790 }
00791 }
00792 }
00793
00794 if (containerID == INVALID_OBJECT_ID)
00795 {
00796 if (!check_path(adir->getLocation(), true))
00797 {
00798 adir->setObjectID(INVALID_OBJECT_ID);
00799 storage->updateAutoscanDirectory(adir);
00800 if (adir->persistent())
00801 {
00802 return;
00803 }
00804 else
00805 {
00806 adir->setTaskCount(-1);
00807 removeAutoscanDirectory(scanID, scanMode);
00808 storage->removeAutoscanDirectory(adir->getStorageID());
00809 return;
00810 }
00811 }
00812
00813 containerID = ensurePathExistence(adir->getLocation());
00814 adir->setObjectID(containerID);
00815 storage->updateAutoscanDirectory(adir);
00816 location = adir->getLocation();
00817 }
00818
00819 time_t last_modified_current_max = adir->getPreviousLMT();
00820
00821 log_debug("Rescanning location: %s\n", location.c_str());
00822
00823 if (!string_ok(location))
00824 {
00825 log_error("Container with ID %d has no location information\n", containerID);
00826 return;
00827
00828
00829 }
00830
00831 DIR *dir = opendir(location.c_str());
00832 if (!dir)
00833 {
00834 log_warning("Could not open %s: %s\n", location.c_str(), strerror(errno));
00835 if (adir->persistent())
00836 {
00837 removeObject(containerID, false);
00838 adir->setObjectID(INVALID_OBJECT_ID);
00839 storage->updateAutoscanDirectory(adir);
00840 return;
00841 }
00842 else
00843 {
00844 adir->setTaskCount(-1);
00845 removeObject(containerID, false);
00846 removeAutoscanDirectory(scanID, scanMode);
00847 storage->removeAutoscanDirectory(adir->getStorageID());
00848 return;
00849 }
00850 }
00851
00852
00853 Ref<DBRHash<int> > list = storage->getObjects(containerID, !adir->getRecursive());
00854
00855 unsigned int thisTaskID;
00856 if (task != nil)
00857 {
00858 thisTaskID = task->getID();
00859 }
00860 else
00861 thisTaskID = 0;
00862
00863 while (((dent = readdir(dir)) != NULL) && (!shutdownFlag) && (task == nil || ((task != nil) && task->isValid())))
00864 {
00865 char *name = dent->d_name;
00866 if (name[0] == '.')
00867 {
00868 if (name[1] == 0)
00869 {
00870 continue;
00871 }
00872 else if (name[1] == '.' && name[2] == 0)
00873 {
00874 continue;
00875 }
00876 else if (!adir->getHidden())
00877 {
00878 continue;
00879 }
00880 }
00881
00882 path = location + DIR_SEPARATOR + name;
00883 ret = stat(path.c_str(), &statbuf);
00884 if (ret != 0)
00885 {
00886 log_error("Failed to stat %s, %s\n", path.c_str(), mt_strerror(errno).c_str());
00887 continue;
00888 }
00889
00890
00891
00892 if (adir->getScanID() == INVALID_SCAN_ID)
00893 {
00894 closedir(dir);
00895 return;
00896 }
00897
00898 if (S_ISREG(statbuf.st_mode))
00899 {
00900 int objectID = storage->findObjectIDByPath(String(path));
00901 if (objectID > 0)
00902 {
00903 if (list != nil)
00904 list->remove(objectID);
00905
00906 if (scanLevel == FullScanLevel)
00907 {
00908
00909 if (last_modified_current_max < statbuf.st_mtime)
00910 {
00911
00912
00913 removeObject(objectID, false);
00914 addFileInternal(path, location, false, false, adir->getHidden());
00915
00916 last_modified_current_max = statbuf.st_mtime;
00917 }
00918 }
00919 else if (scanLevel == BasicScanLevel)
00920 continue;
00921 else
00922 throw _Exception(_("Unsupported scan level!"));
00923
00924 }
00925 else
00926 {
00927
00928
00929 if (ConfigManager::getInstance()->getConfigFilename() != path)
00930 {
00931 addFileInternal(path, location, false, false, adir->getHidden());
00932 if (last_modified_current_max < statbuf.st_mtime)
00933 last_modified_current_max = statbuf.st_mtime;
00934 }
00935 }
00936 }
00937 else if (S_ISDIR(statbuf.st_mode) && (adir->getRecursive()))
00938 {
00939 int objectID = storage->findObjectIDByPath(path + DIR_SEPARATOR);
00940 if (objectID > 0)
00941 {
00942 if (list != nil)
00943 list->remove(objectID);
00944
00945 rescanDirectory(objectID, scanID, scanMode, path + DIR_SEPARATOR, task->isCancellable());
00946 }
00947 else
00948 {
00949
00950
00951
00952
00953
00954 AUTOLOCK(mutex);
00955
00956
00957
00958 if (adir->getScanID() == INVALID_SCAN_ID)
00959 {
00960 closedir(dir);
00961 return;
00962 }
00963
00964
00965 addFileInternal(path, location, true, true, adir->getHidden(), true, thisTaskID, task->isCancellable());
00966 }
00967 }
00968 }
00969
00970 closedir(dir);
00971
00972 if ((shutdownFlag) || ((task != nil) && !task->isValid()))
00973 return;
00974
00975 if (list != nil && list->size() > 0)
00976 {
00977 Ref<Storage::ChangedContainers> changedContainers = storage->removeObjects(list);
00978 if (changedContainers != nil)
00979 {
00980 SessionManager::getInstance()->containerChangedUI(changedContainers->ui);
00981 UpdateManager::getInstance()->containersChanged(changedContainers->upnp);
00982 }
00983 }
00984
00985
00986 adir->setCurrentLMT(last_modified_current_max);
00987 }
00988
00989
00990 void ContentManager::addRecursive(String path, bool hidden, Ref<GenericTask> task)
00991 {
00992 if (hidden == false)
00993 {
00994 log_debug("Checking path %s\n", path.c_str());
00995 if (path.charAt(0) == '.')
00996 return;
00997 }
00998
00999 Ref<StringConverter> f2i = StringConverter::f2i();
01000
01001 Ref<UpdateManager> um = UpdateManager::getInstance();
01002 Ref<Storage> storage = Storage::getInstance();
01003 DIR *dir = opendir(path.c_str());
01004 if (!dir)
01005 {
01006 throw _Exception(_("could not list directory ")+
01007 path + " : " + strerror(errno));
01008 }
01009 int parentID = storage->findObjectIDByPath(path + DIR_SEPARATOR);
01010 struct dirent *dent;
01011
01012
01013 if (task != nil)
01014 {
01015 log_debug("IS TASK VALID? [%d], taskoath: [%s]\n", task->isValid(), path.c_str());
01016 }
01017 while (((dent = readdir(dir)) != NULL) && (!shutdownFlag) && (task == nil || ((task != nil) && task->isValid())))
01018 {
01019 char *name = dent->d_name;
01020 if (name[0] == '.')
01021 {
01022 if (name[1] == 0)
01023 {
01024 continue;
01025 }
01026 else if (name[1] == '.' && name[2] == 0)
01027 {
01028 continue;
01029 }
01030 else if (hidden == false)
01031 continue;
01032 }
01033 String newPath = path + DIR_SEPARATOR + name;
01034
01035 if (ConfigManager::getInstance()->getConfigFilename() == newPath)
01036 continue;
01037
01038 try
01039 {
01040 Ref<CdsObject> obj = nil;
01041 if (parentID > 0)
01042 obj = storage->findObjectByPath(String(newPath));
01043 if (obj == nil)
01044 {
01045 obj = createObjectFromFile(newPath);
01046
01047 if (obj == nil)
01048 {
01049 log_warning("file ignored: %s\n", newPath.c_str());
01050 }
01051 else
01052 {
01053
01054 if (IS_CDS_ITEM(obj->getObjectType()))
01055 {
01056 addObject(obj);
01057 parentID = obj->getParentID();
01058 }
01059 }
01060 }
01061 if (obj != nil)
01062 {
01063
01064 if (IS_CDS_ITEM(obj->getObjectType()))
01065 {
01066 if (layout != nil)
01067 {
01068 try
01069 {
01070 String rootpath = nil;
01071 if (task != nil)
01072 rootpath = RefCast(task, CMAddFileTask)->getRootPath();
01073 layout->processCdsObject(obj, rootpath);
01074 #ifdef HAVE_JS
01075 Ref<Dictionary> mappings = ConfigManager::getInstance()->getDictionaryOption(CFG_IMPORT_MAPPINGS_MIMETYPE_TO_CONTENTTYPE_LIST);
01076 String mimetype = RefCast(obj, CdsItem)->getMimeType();
01077 String content_type = mappings->get(mimetype);
01078
01079 if ((playlist_parser_script != nil) &&
01080 (content_type == CONTENT_TYPE_PLAYLIST))
01081 playlist_parser_script->processPlaylistObject(obj, task);
01082 #ifdef HAVE_LIBDVDNAV
01083 if ((dvd_import_script != nil) &&
01084 (content_type == CONTENT_TYPE_DVD))
01085 dvd_import_script->processDVDObject(obj);
01086 #endif // DVD
01087
01088 #endif // JS
01089 }
01090 catch (Exception e)
01091 {
01092 throw e;
01093 }
01094 }
01095
01097
01098 }
01099
01100 if (IS_CDS_CONTAINER(obj->getObjectType()))
01101 {
01102 addRecursive(newPath, hidden, task);
01103 }
01104 }
01105 }
01106 catch(Exception e)
01107 {
01108 log_warning("skipping %s : %s\n", newPath.c_str(), e.getMessage().c_str());
01109 }
01110 }
01111 closedir(dir);
01112 }
01113
01114 void ContentManager::updateObject(int objectID, Ref<Dictionary> parameters)
01115 {
01116 String title = parameters->get(_("title"));
01117 String upnp_class = parameters->get(_("class"));
01118 String autoscan = parameters->get(_("autoscan"));
01119 String mimetype = parameters->get(_("mime-type"));
01120 String description = parameters->get(_("description"));
01121 String location = parameters->get(_("location"));
01122 String protocol = parameters->get(_("protocol"));
01123
01124 Ref<Storage> storage = Storage::getInstance();
01125 Ref<UpdateManager> um = UpdateManager::getInstance();
01126 Ref<SessionManager> sm = SessionManager::getInstance();
01127
01128 Ref<CdsObject> obj = storage->loadObject(objectID);
01129 int objectType = obj->getObjectType();
01130
01132 if (IS_CDS_ITEM(objectType))
01133 {
01134 Ref<CdsItem> item = RefCast(obj, CdsItem);
01135 Ref<CdsObject> clone = CdsObject::createObject(objectType);
01136 item->copyTo(clone);
01137
01138 if (string_ok(title)) clone->setTitle(title);
01139 if (string_ok(upnp_class)) clone->setClass(upnp_class);
01140 if (string_ok(location)) clone->setLocation(location);
01141
01142 Ref<CdsItem> cloned_item = RefCast(clone, CdsItem);
01143
01144 if (string_ok(mimetype) && (string_ok(protocol)))
01145 {
01146 cloned_item->setMimeType(mimetype);
01147 Ref<CdsResource> resource = cloned_item->getResource(0);
01148 resource->addAttribute(_("protocolInfo"), renderProtocolInfo(mimetype, protocol));
01149 }
01150 else if (!string_ok(mimetype) && (string_ok(protocol)))
01151 {
01152 Ref<CdsResource> resource = cloned_item->getResource(0);
01153 resource->addAttribute(_("protocolInfo"), renderProtocolInfo(cloned_item->getMimeType(), protocol));
01154 }
01155 else if (string_ok(mimetype) && (!string_ok(protocol)))
01156 {
01157 cloned_item->setMimeType(mimetype);
01158 Ref<CdsResource> resource = cloned_item->getResource(0);
01159 Ref<Array<StringBase> > parts = split_string(resource->getAttribute(_("protocolInfo")), ':');
01160 protocol = parts->get(0);
01161 resource->addAttribute(_("protocolInfo"), renderProtocolInfo(mimetype, protocol));
01162 }
01163
01164 if (string_ok(description))
01165 {
01166 cloned_item->setMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION),
01167 description);
01168 }
01169 else
01170 {
01171 cloned_item->removeMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION));
01172 }
01173
01174
01175 log_debug("updateObject: checking equality of item %s\n", item->getTitle().c_str());
01176 if (!item->equals(clone, true))
01177 {
01178 cloned_item->validate();
01179 int containerChanged = INVALID_OBJECT_ID;
01180 storage->updateObject(clone, &containerChanged);
01181 um->containerChanged(containerChanged);
01182 sm->containerChangedUI(containerChanged);
01183 log_debug("updateObject: calling containerChanged on item %s\n", item->getTitle().c_str());
01184 um->containerChanged(item->getParentID());
01185 }
01186 }
01187 if (IS_CDS_ACTIVE_ITEM(objectType))
01188 {
01189 String action = parameters->get(_("action"));
01190 String state = parameters->get(_("state"));
01191
01192 Ref<CdsActiveItem> item = RefCast(obj, CdsActiveItem);
01193 Ref<CdsObject> clone = CdsObject::createObject(objectType);
01194 item->copyTo(clone);
01195
01196 if (string_ok(title)) clone->setTitle(title);
01197 if (string_ok(upnp_class)) clone->setClass(upnp_class);
01198
01199 Ref<CdsActiveItem> cloned_item = RefCast(clone, CdsActiveItem);
01200
01201
01202 if (string_ok(description))
01203 {
01204 cloned_item->setMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION),
01205 description);
01206 }
01207 else
01208 {
01209 cloned_item->removeMetadata(MetadataHandler::getMetaFieldName(M_DESCRIPTION));
01210 }
01211
01212 if (state != nil) cloned_item->setState(state);
01213
01214 if (string_ok(mimetype)) cloned_item->setMimeType(mimetype);
01215 if (string_ok(action)) cloned_item->setAction(action);
01216
01217 if (!item->equals(clone, true))
01218 {
01219 cloned_item->validate();
01220 int containerChanged = INVALID_OBJECT_ID;
01221 storage->updateObject(clone, &containerChanged);
01222 um->containerChanged(containerChanged);
01223 sm->containerChangedUI(containerChanged);
01224 um->containerChanged(item->getParentID());
01225 }
01226 }
01227 else if (IS_CDS_CONTAINER(objectType))
01228 {
01229 Ref<CdsContainer> cont = RefCast(obj, CdsContainer);
01230 Ref<CdsObject> clone = CdsObject::createObject(objectType);
01231 cont->copyTo(clone);
01232
01233 if (string_ok(title)) clone->setTitle(title);
01234 if (string_ok(upnp_class)) clone->setClass(upnp_class);
01235
01236 if (!cont->equals(clone, true))
01237 {
01238 clone->validate();
01239 int containerChanged = INVALID_OBJECT_ID;
01240 storage->updateObject(clone, &containerChanged);
01241 um->containerChanged(containerChanged);
01242 sm->containerChangedUI(containerChanged);
01243 um->containerChanged(cont->getParentID());
01244 sm->containerChangedUI(cont->getParentID());
01245 }
01246 }
01247
01248 }
01249
01250 void ContentManager::addObject(zmm::Ref<CdsObject> obj)
01251 {
01252 obj->validate();
01253 int parent_id;
01254 Ref<Storage> storage = Storage::getInstance();
01255 Ref<UpdateManager> um = UpdateManager::getInstance();
01256 Ref<SessionManager> sm = SessionManager::getInstance();
01257 int containerChanged = INVALID_OBJECT_ID;
01258 log_debug("Adding: parent ID is %d\n", obj->getParentID());
01259 if (!IS_CDS_ITEM_EXTERNAL_URL(obj->getObjectType()))
01260 {
01261 obj->setLocation(obj->getLocation().reduce(DIR_SEPARATOR));
01262 }
01263 storage->addObject(obj, &containerChanged);
01264 log_debug("After adding: parent ID is %d\n", obj->getParentID());
01265
01266 um->containerChanged(containerChanged);
01267 sm->containerChangedUI(containerChanged);
01268
01269 parent_id = obj->getParentID();
01270 if ((parent_id != -1) && (storage->getChildCount(parent_id) == 1))
01271 {
01272 Ref<CdsObject> parent;
01273 parent = storage->loadObject(parent_id);
01274 log_debug("Will update ID %d\n", parent->getParentID());
01275 um->containerChanged(parent->getParentID());
01276 }
01277
01278 um->containerChanged(obj->getParentID());
01279 if (IS_CDS_CONTAINER(obj->getObjectType()))
01280 sm->containerChangedUI(obj->getParentID());
01281
01282 if (! obj->isVirtual() && IS_CDS_ITEM(obj->getObjectType()))
01283 ContentManager::getInstance()->getAccounting()->totalFiles++;
01284 }
01285
01286
01287 void ContentManager::addContainer(int parentID, String title, String upnpClass)
01288 {
01289 Ref<Storage> storage = Storage::getInstance();
01290 addContainerChain(storage->buildContainerPath(parentID, escape(title, VIRTUAL_CONTAINER_ESCAPE, VIRTUAL_CONTAINER_SEPARATOR)), upnpClass);
01291 }
01292
01293
01294 int ContentManager::addContainerChain(String chain, String lastClass, int lastRefID)
01295 {
01296 Ref<Storage> storage = Storage::getInstance();
01297 int updateID = INVALID_OBJECT_ID;
01298 int containerID;
01299
01300 if (!string_ok(chain))
01301 throw _Exception(_("addContainerChain() called with empty chain parameter"));
01302
01303 log_debug("received chain: %s\n", chain.c_str());
01304 storage->addContainerChain(chain, lastClass, lastRefID,
01305 &containerID, &updateID);
01306
01307
01308 UpdateManager::getInstance()->containerChanged(updateID);
01309 SessionManager::getInstance()->containerChangedUI(updateID);
01310
01311 return containerID;
01312 }
01313
01314 void ContentManager::updateObject(Ref<CdsObject> obj, bool send_updates)
01315 {
01316 obj->validate();
01317 Ref<Storage> storage = Storage::getInstance();
01318
01319 int containerChanged = INVALID_OBJECT_ID;
01320 storage->updateObject(obj, &containerChanged);
01321
01322 if (send_updates)
01323 {
01324 Ref<UpdateManager> um = UpdateManager::getInstance();
01325 Ref<SessionManager> sm = SessionManager::getInstance();
01326
01327 um->containerChanged(containerChanged);
01328 sm->containerChangedUI(containerChanged);
01329
01330 um->containerChanged(obj->getParentID());
01331 if (IS_CDS_CONTAINER(obj->getObjectType()))
01332 sm->containerChangedUI(obj->getParentID());
01333 }
01334 }
01335
01336 Ref<CdsObject> ContentManager::convertObject(Ref<CdsObject> oldObj, int newType)
01337 {
01338 int oldType = oldObj->getObjectType();
01339 if (oldType == newType)
01340 return oldObj;
01341 if (! IS_CDS_ITEM(oldType) || ! IS_CDS_ITEM(newType))
01342 {
01343 throw _Exception(_("Cannot convert object type ") + oldType +
01344 " to " + newType);
01345 }
01346
01347 Ref<CdsObject> newObj = CdsObject::createObject(newType);
01348
01349 oldObj->copyTo(newObj);
01350
01351 return newObj;
01352 }
01353
01354
01355 Ref<CdsObject> ContentManager::createObjectFromFile(String path, bool magic, bool allow_fifo)
01356 {
01357 String filename = get_filename(path);
01358
01359 struct stat statbuf;
01360 int ret;
01361
01362 ret = stat(path.c_str(), &statbuf);
01363 if (ret != 0)
01364 {
01365 throw _Exception(_("Failed to stat ") + path + _(" , ") + mt_strerror(errno));
01366 }
01367
01368 Ref<CdsObject> obj;
01369 if (S_ISREG(statbuf.st_mode) || (allow_fifo && S_ISFIFO(statbuf.st_mode)))
01370 {
01371
01372
01373 String mimetype;
01374 String upnp_class;
01375 String extension;
01376
01377
01378 int dotIndex = filename.rindex('.');
01379 if (dotIndex > 0)
01380 extension = filename.substring(dotIndex + 1);
01381
01382 if (magic)
01383 mimetype = extension2mimetype(extension);
01384
01385 if (mimetype == nil && magic)
01386 {
01387 if (ignore_unknown_extensions)
01388 return nil;
01389 #ifdef HAVE_MAGIC
01390 mimetype = get_mime_type(ms, reMimetype, path);
01391 #endif
01392 }
01393 if (mimetype != nil)
01394 {
01395 upnp_class = mimetype2upnpclass(mimetype);
01396 }
01397
01398 if (!string_ok(upnp_class))
01399 {
01400 String content_type = mimetype_contenttype_map->get(mimetype);
01401 if (content_type == CONTENT_TYPE_OGG)
01402 {
01403 if (isTheora(path))
01404 upnp_class = _(UPNP_DEFAULT_CLASS_VIDEO_ITEM);
01405 else
01406 upnp_class = _(UPNP_DEFAULT_CLASS_MUSIC_TRACK);
01407 }
01408 }
01409
01410 Ref<CdsItem> item(new CdsItem());
01411 obj = RefCast(item, CdsObject);
01412 item->setLocation(path);
01413 if (mimetype != nil)
01414 item->setMimeType(mimetype);
01415 if (upnp_class != nil)
01416 item->setClass(upnp_class);
01417 Ref<StringConverter> f2i = StringConverter::f2i();
01418 obj->setTitle(f2i->convert(filename));
01419 if (magic)
01420 MetadataHandler::setMetadata(item);
01421 }
01422 else if (S_ISDIR(statbuf.st_mode))
01423 {
01424 Ref<CdsContainer> cont(new CdsContainer());
01425 obj = RefCast(cont, CdsObject);
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435 }
01436 else
01437 {
01438
01439 throw _Exception(_("ContentManager: skipping file ") + path.c_str());
01440 }
01441
01442
01443 return obj;
01444 }
01445
01446 String ContentManager::extension2mimetype(String extension)
01447 {
01448 if (extension_mimetype_map == nil)
01449 return nil;
01450
01451 if (!extension_map_case_sensitive)
01452 extension = extension.toLower();
01453
01454 return extension_mimetype_map->get(extension);
01455 }
01456
01457 String ContentManager::mimetype2upnpclass(String mimeType)
01458 {
01459 if (mimetype_upnpclass_map == nil)
01460 return nil;
01461 String upnpClass = mimetype_upnpclass_map->get(mimeType);
01462 if (upnpClass != nil)
01463 return upnpClass;
01464
01465 Ref<Array<StringBase> > parts = split_string(mimeType, '/');
01466 if (parts->size() != 2)
01467 return nil;
01468 return mimetype_upnpclass_map->get((String)parts->get(0) + "/*");
01469 }
01470
01471 void ContentManager::initLayout()
01472 {
01473
01474 if (layout == nil)
01475 {
01476 AUTOLOCK(mutex);
01477 if (layout == nil)
01478 try
01479 {
01480 String layout_type =
01481 ConfigManager::getInstance()->getOption(CFG_IMPORT_SCRIPTING_VIRTUAL_LAYOUT_TYPE);
01482 if (layout_type == "js")
01483 {
01484 #ifdef HAVE_JS
01485 layout = Ref<Layout>((Layout *)new JSLayout());
01486 #else
01487 log_error("Cannot init layout: MediaTomb compiled without js support but js script was requested.");
01488 #endif
01489 }
01490 else if (layout_type == "builtin")
01491 {
01492 layout = Ref<Layout>((FallbackLayout *)new FallbackLayout());
01493 }
01494 }
01495 catch (Exception e)
01496 {
01497 layout = nil;
01498 log_error("ContentManager virtual container layout: %s\n", e.getMessage().c_str());
01499 }
01500 }
01501 }
01502
01503 #ifdef HAVE_JS
01504 void ContentManager::initJS()
01505 {
01506 if (playlist_parser_script == nil)
01507 playlist_parser_script = Ref<PlaylistParserScript>(new PlaylistParserScript(Runtime::getInstance()));
01508
01509 #ifdef HAVE_LIBDVDNAV
01510 if ((ConfigManager::getInstance()->getOption(CFG_IMPORT_SCRIPTING_VIRTUAL_LAYOUT_TYPE) == "js") && (dvd_import_script == nil))
01511 {
01512 dvd_import_script = Ref<DVDImportScript>(new DVDImportScript(Runtime::getInstance()));
01513 }
01514 #endif
01515 }
01516
01517 void ContentManager::destroyJS()
01518 {
01519 playlist_parser_script = nil;
01520 #ifdef HAVE_LIBDVDNAV
01521 dvd_import_script = nil;
01522 #endif
01523 }
01524
01525 #endif // HAVE_JS
01526
01527 void ContentManager::destroyLayout()
01528 {
01529 layout = nil;
01530 }
01531
01532 void ContentManager::reloadLayout()
01533 {
01534 destroyLayout();
01535 initLayout();
01536 }
01537
01538 void ContentManager::threadProc()
01539 {
01540 Ref<GenericTask> task;
01541 AUTOLOCK(mutex);
01542 working = true;
01543 while(! shutdownFlag)
01544 {
01545 currentTask = nil;
01546 if(((task = taskQueue1->dequeue()) == nil) && ((task = taskQueue2->dequeue()) == nil))
01547 {
01548 working = false;
01549
01550 cond->wait();
01551 working = true;
01552 continue;
01553 }
01554 else
01555 {
01556 currentTask = task;
01557 }
01558 AUTOUNLOCK();
01559
01560
01561 try
01562 {
01563 if (task->isValid())
01564 task->run();
01565 }
01566 catch (ServerShutdownException se)
01567 {
01568 shutdownFlag = true;
01569 }
01570 catch (Exception e)
01571 {
01572 log_error("Exception caught: %s\n", e.getMessage().c_str());
01573 e.printStackTrace();
01574 }
01575
01576 if (! shutdownFlag)
01577 AUTORELOCK();
01578 }
01579
01580 Storage::getInstance()->threadCleanup();
01581 }
01582
01583 void *ContentManager::staticThreadProc(void *arg)
01584 {
01585 ContentManager *inst = (ContentManager *)arg;
01586 inst->threadProc();
01587 pthread_exit(NULL);
01588 return NULL;
01589 }
01590
01591 void ContentManager::addTask(zmm::Ref<GenericTask> task, bool lowPriority)
01592 {
01593 AUTOLOCK(mutex);
01594
01595 task->setID(taskID++);
01596
01597 if (! lowPriority)
01598 taskQueue1->enqueue(task);
01599 else
01600 taskQueue2->enqueue(task);
01601 signal();
01602 }
01603
01604
01605 void ContentManager::loadAccounting(bool async)
01606 {
01607 if (async)
01608 {
01609 Ref<GenericTask> task(new CMLoadAccountingTask());
01610 task->setDescription(_("Initializing statistics"));
01611 addTask(task);
01612 }
01613 else
01614 {
01615 _loadAccounting();
01616 }
01617 }
01618
01619 int ContentManager::addFile(zmm::String path, bool recursive, bool async,
01620 bool hidden, bool lowPriority, bool cancellable)
01621 {
01622 String rootpath;
01623 if (check_path(path, true))
01624 rootpath = path;
01625 return addFileInternal(path, rootpath, recursive, async, hidden, lowPriority, 0, cancellable);
01626 }
01627
01628 int ContentManager::addFileInternal(String path, String rootpath,
01629 bool recursive,
01630 bool async, bool hidden, bool lowPriority,
01631 unsigned int parentTaskID,
01632 bool cancellable)
01633 {
01634 if (async)
01635 {
01636 Ref<GenericTask> task(new CMAddFileTask(path, rootpath, recursive, hidden, cancellable));
01637 task->setDescription(_("Adding: ") + path);
01638 task->setParentID(parentTaskID);
01639 addTask(task, lowPriority);
01640 return INVALID_OBJECT_ID;
01641 }
01642 else
01643 {
01644 return _addFile(path, rootpath, recursive, hidden);
01645 }
01646 }
01647
01648 #ifdef ONLINE_SERVICES
01649 void ContentManager::fetchOnlineContent(service_type_t service,
01650 bool lowPriority, bool cancellable,
01651 bool unscheduled_refresh)
01652 {
01653 Ref<OnlineService> os = online_services->getService(service);
01654 if (os == nil)
01655 {
01656 log_debug("No surch service! %d\n", service);
01657 throw _Exception(_("Service not found!"));
01658 }
01659 fetchOnlineContentInternal(os, lowPriority, cancellable, 0,
01660 unscheduled_refresh);
01661 }
01662
01663 void ContentManager::fetchOnlineContentInternal(Ref<OnlineService> service,
01664 bool lowPriority, bool cancellable,
01665 unsigned int parentTaskID,
01666 bool unscheduled_refresh)
01667 {
01668 if (layout_enabled)
01669 initLayout();
01670
01671 Ref<GenericTask> task(new CMFetchOnlineContentTask(service, layout, cancellable,
01672 unscheduled_refresh));
01673 task->setDescription(_("Updating content from ") +
01674 service->getServiceName());
01675 task->setParentID(parentTaskID);
01676 service->incTaskCount();
01677 addTask(task, lowPriority);
01678 }
01679
01680 void ContentManager::_fetchOnlineContent(Ref<OnlineService> service,
01681 unsigned int parentTaskID,
01682 bool unscheduled_refresh)
01683 {
01684 throw _Exception(_("Should not be called anymore!"));
01685 log_debug("Fetching online content!\n");
01686 if (layout_enabled)
01687 initLayout();
01688
01689 if (service->refreshServiceData(layout) && (!shutdownFlag))
01690 {
01691 log_debug("Scheduling another task for online service: %s\n",
01692 service->getServiceName().c_str());
01693
01694 if ((service->getRefreshInterval() > 0) || unscheduled_refresh)
01695 fetchOnlineContentInternal(service, true, true, parentTaskID,
01696 unscheduled_refresh);
01697 }
01698 else
01699 {
01700 log_debug("Finished fetch cycle for service: %s\n",
01701 service->getServiceName().c_str());
01702
01703 if (service->getItemPurgeInterval() > 0)
01704 {
01705 Ref<Storage> storage = Storage::getInstance();
01706 Ref<IntArray> ids = storage->getServiceObjectIDs(service->getStoragePrefix());
01707
01708 struct timespec current, last;
01709 getTimespecNow(¤t);
01710 last.tv_nsec = 0;
01711 String temp;
01712
01713 for (int i = 0; i < ids->size(); i++)
01714 {
01715 int object_id = ids->get(i);
01716 Ref<CdsObject> obj = storage->loadObject(object_id);
01717 if (obj == nil)
01718 continue;
01719
01720 temp = obj->getAuxData(_(ONLINE_SERVICE_LAST_UPDATE));
01721 if (!string_ok(temp))
01722 continue;
01723
01724 last.tv_sec = temp.toLong();
01725
01726 if ((service->getItemPurgeInterval() > 0) &&
01727 ((current.tv_sec - last.tv_sec) > service->getItemPurgeInterval()))
01728 {
01729 log_debug("Purging old online service object %s\n",
01730 obj->getTitle().c_str());
01731 removeObject(object_id, false);
01732 }
01733 }
01734 }
01735 }
01736 }
01737
01738 void ContentManager::cleanupOnlineServiceObjects(zmm::Ref<OnlineService> service)
01739 {
01740 log_debug("Finished fetch cycle for service: %s\n",
01741 service->getServiceName().c_str());
01742
01743 if (service->getItemPurgeInterval() > 0)
01744 {
01745 Ref<Storage> storage = Storage::getInstance();
01746 Ref<IntArray> ids = storage->getServiceObjectIDs(service->getStoragePrefix());
01747
01748 struct timespec current, last;
01749 getTimespecNow(¤t);
01750 last.tv_nsec = 0;
01751 String temp;
01752
01753 for (int i = 0; i < ids->size(); i++)
01754 {
01755 int object_id = ids->get(i);
01756 Ref<CdsObject> obj = storage->loadObject(object_id);
01757 if (obj == nil)
01758 continue;
01759
01760 temp = obj->getAuxData(_(ONLINE_SERVICE_LAST_UPDATE));
01761 if (!string_ok(temp))
01762 continue;
01763
01764 last.tv_sec = temp.toLong();
01765
01766 if ((service->getItemPurgeInterval() > 0) &&
01767 ((current.tv_sec - last.tv_sec) > service->getItemPurgeInterval()))
01768 {
01769 log_debug("Purging old online service object %s\n",
01770 obj->getTitle().c_str());
01771 removeObject(object_id, false);
01772 }
01773 }
01774 }
01775 }
01776
01777 #endif
01778
01779 void ContentManager::invalidateAddTask(Ref<GenericTask> t, String path)
01780 {
01781 if (t->getType() == AddFile)
01782 {
01783 log_debug("comparing, task path: %s, remove path: %s\n", RefCast(t, CMAddFileTask)->getPath().c_str(), path.c_str());
01784 if ((RefCast(t, CMAddFileTask)->getPath().startsWith(path)))
01785 {
01786 log_debug("Invalidating task with path %s\n", RefCast(t, CMAddFileTask)->getPath().c_str());
01787 t->invalidate();
01788 }
01789 }
01790 }
01791
01792 void ContentManager::invalidateTask(unsigned int taskID, task_owner_t taskOwner)
01793 {
01794 int i;
01795
01796 if (taskOwner == ContentManagerTask)
01797 {
01798 AUTOLOCK(mutex);
01799 Ref<GenericTask> t = getCurrentTask();
01800 if (t != nil)
01801 {
01802 if ((t->getID() == taskID) || (t->getParentID() == taskID))
01803 {
01804 t->invalidate();
01805 }
01806 }
01807
01808 int qsize = taskQueue1->size();
01809
01810 for (i = 0; i < qsize; i++)
01811 {
01812 Ref<GenericTask> t = taskQueue1->get(i);
01813 if ((t->getID() == taskID) || (t->getParentID() == taskID))
01814 {
01815 t->invalidate();
01816 }
01817 }
01818
01819 qsize = taskQueue2->size();
01820 for (i = 0; i < qsize; i++)
01821 {
01822 Ref<GenericTask> t = taskQueue2->get(i);
01823 if ((t->getID() == taskID) || (t->getParentID() == taskID))
01824 {
01825 t->invalidate();
01826 }
01827 }
01828 }
01829 #ifdef ONLINE_SERVICES
01830 else if (taskOwner == TaskProcessorTask)
01831 TaskProcessor::getInstance()->invalidateTask(taskID);
01832 #endif
01833 }
01834
01835 void ContentManager::removeObject(int objectID, bool async, bool all)
01836 {
01837 if (async)
01838 {
01839
01840
01841
01842
01843
01844
01845
01846
01847
01848
01849 Ref<GenericTask> task(new CMRemoveObjectTask(objectID, all));
01850 Ref<Storage> storage = Storage::getInstance();
01851 String path;
01852 Ref<CdsObject> obj;
01853
01854 try
01855 {
01856 obj = storage->loadObject(objectID);
01857 path = obj->getLocation();
01858
01859 String vpath = obj->getVirtualPath();
01860 if (string_ok(vpath))
01861 task->setDescription(_("Removing: ") + obj->getVirtualPath());
01862 }
01863 catch (Exception e)
01864 {
01865 log_debug("trying to remove an object ID which is no longer in the database! %d\n", objectID);
01866 return;
01867 }
01868
01869 if (IS_CDS_CONTAINER(obj->getObjectType()))
01870 {
01871 int i;
01872
01873
01874 Ref<AutoscanList> rm_list = autoscan_timed->removeIfSubdir(path);
01875 for (i = 0; i < rm_list->size(); i++)
01876 {
01877 Timer::getInstance()->removeTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON(this), rm_list->get(i)->getTimerParameter(), true);
01878 }
01879 #ifdef HAVE_INOTIFY
01880 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
01881 {
01882 rm_list = autoscan_inotify->removeIfSubdir(path);
01883 for (i = 0; i < rm_list->size(); i++)
01884 {
01885 Ref<AutoscanDirectory> dir = rm_list->get(i);
01886 inotify->unmonitor(dir);
01887 }
01888 }
01889 #endif
01890
01891 AUTOLOCK(mutex);
01892 int qsize = taskQueue1->size();
01893
01894
01895
01896 for (i = 0; i < qsize; i++)
01897 {
01898 Ref<GenericTask> t = taskQueue1->get(i);
01899 invalidateAddTask(t, path);
01900 }
01901
01902 qsize = taskQueue2->size();
01903 for (i = 0; i < qsize; i++)
01904 {
01905 Ref<GenericTask> t = taskQueue2->get(i);
01906 invalidateAddTask(t, path);
01907 }
01908
01909 Ref<GenericTask> t = getCurrentTask();
01910 if (t != nil)
01911 {
01912 invalidateAddTask(t, path);
01913 }
01914 }
01915
01916 addTask(task);
01917 }
01918 else
01919 {
01920 _removeObject(objectID, all);
01921 }
01922 }
01923
01924 void ContentManager::rescanDirectory(int objectID, int scanID, scan_mode_t scanMode, String descPath, bool cancellable)
01925 {
01926
01927 Ref<GenericTask> task(new CMRescanDirectoryTask(objectID, scanID, scanMode, cancellable));
01928 Ref<AutoscanDirectory> dir = getAutoscanDirectory(scanID, scanMode);
01929 if (dir == nil)
01930 return;
01931
01932 dir->incTaskCount();
01933 String level;
01934 if (dir->getScanLevel() == BasicScanLevel)
01935 level = _("basic");
01936 else
01937 level = _("full");
01938
01939 if (!string_ok(descPath))
01940 descPath = dir->getLocation();
01941
01942 task->setDescription(_("Performing ") + level + " scan: " + descPath);
01943 addTask(task, true);
01944 }
01945
01946
01947 Ref<AutoscanDirectory> ContentManager::getAutoscanDirectory(int scanID, scan_mode_t scanMode)
01948 {
01949 if (scanMode == TimedScanMode)
01950 {
01951 return autoscan_timed->get(scanID);
01952 }
01953
01954 #if HAVE_INOTIFY
01955 else if (scanMode == InotifyScanMode)
01956 {
01957 return autoscan_inotify->get(scanID);
01958 }
01959 #endif
01960 return nil;
01961 }
01962
01963 Ref<Array<AutoscanDirectory> > ContentManager::getAutoscanDirectories(scan_mode_t scanMode)
01964 {
01965 if (scanMode == TimedScanMode)
01966 {
01967 return autoscan_timed->getArrayCopy();
01968 }
01969
01970 #if HAVE_INOTIFY
01971 else if (scanMode == InotifyScanMode)
01972 {
01973 return autoscan_inotify->getArrayCopy();
01974 }
01975 #endif
01976 return nil;
01977 }
01978
01979 Ref<Array<AutoscanDirectory> > ContentManager::getAutoscanDirectories()
01980 {
01981 Ref<Array<AutoscanDirectory> > all = autoscan_timed->getArrayCopy();
01982
01983 #if HAVE_INOTIFY
01984 Ref<Array<AutoscanDirectory> > ino = autoscan_inotify->getArrayCopy();
01985 if (ino != nil)
01986 for (int i = 0; i < ino->size(); i ++)
01987 all->append(ino->get(i));
01988 #endif
01989 return all;
01990 }
01991
01992 Ref<AutoscanDirectory> ContentManager::getAutoscanDirectory(String location)
01993 {
01994
01995 Ref<AutoscanDirectory> dir = autoscan_timed->get(location);
01996 #if HAVE_INOTIFY
01997 if (dir == nil)
01998 dir = autoscan_inotify->get(location);
01999 #endif
02000 return dir;
02001 }
02002
02003 void ContentManager::removeAutoscanDirectory(int scanID, scan_mode_t scanMode)
02004 {
02005 if (scanMode == TimedScanMode)
02006 {
02007 Ref<Storage> storage = Storage::getInstance();
02008 Ref<AutoscanDirectory> adir = autoscan_timed->get(scanID);
02009 if (adir == nil)
02010 throw _Exception(_("can not remove autoscan directory - was not an autoscan"));
02011
02012 autoscan_timed->remove(scanID);
02013 storage->removeAutoscanDirectory(adir->getStorageID());
02014 SessionManager::getInstance()->containerChangedUI(adir->getObjectID());
02015
02016
02017 Timer::getInstance()->removeTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON(this), adir->getTimerParameter(), true);
02018
02019 }
02020 #ifdef HAVE_INOTIFY
02021 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
02022 {
02023 if (scanMode == InotifyScanMode)
02024 {
02025 Ref<Storage> storage = Storage::getInstance();
02026 Ref<AutoscanDirectory> adir = autoscan_inotify->get(scanID);
02027 if (adir == nil)
02028 throw _Exception(_("can not remove autoscan directory - was not an autoscan"));
02029 autoscan_inotify->remove(scanID);
02030 storage->removeAutoscanDirectory(adir->getStorageID());
02031 SessionManager::getInstance()->containerChangedUI(adir->getObjectID());
02032 inotify->unmonitor(adir);
02033 }
02034 }
02035 #endif
02036
02037 }
02038
02039 void ContentManager::removeAutoscanDirectory(int objectID)
02040 {
02041 Ref<Storage> storage = Storage::getInstance();
02042 Ref<AutoscanDirectory> adir = storage->getAutoscanDirectory(objectID);
02043 if (adir == nil)
02044 throw _Exception(_("can not remove autoscan directory - was not an autoscan"));
02045
02046 if (adir->getScanMode() == TimedScanMode)
02047 {
02048 autoscan_timed->remove(adir->getLocation());
02049 storage->removeAutoscanDirectoryByObjectID(objectID);
02050 SessionManager::getInstance()->containerChangedUI(objectID);
02051 Timer::getInstance()->removeTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON(this), adir->getTimerParameter(), true);
02052 }
02053 #ifdef HAVE_INOTIFY
02054 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
02055 {
02056 if (adir->getScanMode() == InotifyScanMode)
02057 {
02058 autoscan_inotify->remove(adir->getLocation());
02059 storage->removeAutoscanDirectoryByObjectID(objectID);
02060 SessionManager::getInstance()->containerChangedUI(objectID);
02061 inotify->unmonitor(adir);
02062 }
02063 }
02064 #endif
02065 }
02066
02067 void ContentManager::removeAutoscanDirectory(String location)
02068 {
02070 Ref<AutoscanDirectory> adir = autoscan_timed->get(location);
02071 #ifdef HAVE_INOTIFY
02072 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
02073 {
02074 if (adir == nil)
02075 adir = autoscan_inotify->get(location);
02076 }
02077 #endif
02078 if (adir == nil)
02079 throw _Exception(_("can not remove autoscan directory - was not an autoscan"));
02080
02081 removeAutoscanDirectory(adir->getObjectID());
02082 }
02083
02084 void ContentManager::handlePeristentAutoscanRemove(int scanID, scan_mode_t scanMode)
02085 {
02086 Ref<AutoscanDirectory> adir = getAutoscanDirectory(scanID, scanMode);
02087 Ref<Storage> st = Storage::getInstance();
02088 if (adir->persistent())
02089 {
02090 adir->setObjectID(INVALID_OBJECT_ID);
02091 st->updateAutoscanDirectory(adir);
02092 }
02093 else
02094 {
02095 removeAutoscanDirectory(adir->getScanID(), adir->getScanMode());
02096 st->removeAutoscanDirectory(adir->getStorageID());
02097 }
02098 }
02099
02100 void ContentManager::handlePersistentAutoscanRecreate(int scanID, scan_mode_t scanMode)
02101 {
02102 Ref<AutoscanDirectory> adir = getAutoscanDirectory(scanID, scanMode);
02103 int id = ensurePathExistence(adir->getLocation());
02104 adir->setObjectID(id);
02105 Storage::getInstance()->updateAutoscanDirectory(adir);
02106 }
02107
02108 void ContentManager::setAutoscanDirectory(Ref<AutoscanDirectory> dir)
02109 {
02110 int scanID = INVALID_SCAN_ID;
02111 Ref<Storage> storage = Storage::getInstance();
02112 Ref<AutoscanDirectory> original;
02113
02114
02115 original = autoscan_timed->getByObjectID(dir->getObjectID());
02116 #ifdef HAVE_INOTIFY
02117 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
02118 {
02119 if (original == nil)
02120 original = autoscan_inotify->getByObjectID(dir->getObjectID());
02121 }
02122 #endif
02123
02124 if (original != nil)
02125 dir->setStorageID(original->getStorageID());
02126
02127 storage->checkOverlappingAutoscans(dir);
02128
02129
02130 if (original == nil)
02131 {
02132 if (dir->getObjectID() == CDS_ID_FS_ROOT)
02133 dir->setLocation(_(FS_ROOT_DIRECTORY));
02134 else
02135 {
02136 log_debug("objectID: %d\n", dir->getObjectID());
02137 Ref<CdsObject> obj = storage->loadObject(dir->getObjectID());
02138 if (obj == nil
02139 || ! IS_CDS_CONTAINER(obj->getObjectType())
02140 || obj->isVirtual())
02141 throw _Exception(_("tried to remove an illegal object (id) from the list of the autoscan directories"));
02142
02143 log_debug("location: %s\n", obj->getLocation().c_str());
02144
02145 if (!string_ok(obj->getLocation()))
02146 throw _Exception(_("tried to add an illegal object as autoscan - no location information available!"));
02147
02148 dir->setLocation(obj->getLocation());
02149 }
02150 dir->resetLMT();
02151 storage->addAutoscanDirectory(dir);
02152 if (dir->getScanMode() == TimedScanMode)
02153 {
02154 scanID = autoscan_timed->add(dir);
02155 timerNotify(dir->getTimerParameter());
02156 }
02157 #ifdef HAVE_INOTIFY
02158 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
02159 {
02160 if (dir->getScanMode() == InotifyScanMode)
02161 {
02162 autoscan_inotify->add(dir);
02163 inotify->monitor(dir);
02164 }
02165 }
02166 #endif
02167 SessionManager::getInstance()->containerChangedUI(dir->getObjectID());
02168 return;
02169 }
02170
02171 if (original->getScanMode() == TimedScanMode)
02172 Timer::getInstance()->removeTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON(this), original->getTimerParameter(), true);
02173 #ifdef HAVE_INOTIFY
02174 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
02175 {
02176 if (original->getScanMode() == InotifyScanMode)
02177 {
02178 inotify->unmonitor(original);
02179 }
02180 }
02181 #endif
02182
02183 Ref<AutoscanDirectory> copy(new AutoscanDirectory());
02184 original->copyTo(copy);
02185
02186
02187 if ((copy->getScanLevel() == FullScanLevel) && (dir->getScanLevel() == BasicScanLevel))
02188 {
02189 copy->setScanLevel(BasicScanLevel);
02190 copy->resetLMT();
02191 }
02192 else if (((copy->getScanLevel() == FullScanLevel) &&
02193 (dir->getScanLevel() == FullScanLevel)) &&
02194 (!copy->getRecursive() && dir->getRecursive()))
02195 {
02196 copy->resetLMT();
02197 }
02198
02199 copy->setScanLevel(dir->getScanLevel());
02200 copy->setHidden(dir->getHidden());
02201 copy->setRecursive(dir->getRecursive());
02202 copy->setInterval(dir->getInterval());
02203
02204 if (copy->getScanMode() == TimedScanMode)
02205 {
02206 autoscan_timed->remove(copy->getScanID());
02207 }
02208 #ifdef HAVE_INOTIFY
02209 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
02210 {
02211 if (copy->getScanMode() == InotifyScanMode)
02212 {
02213 autoscan_inotify->remove(copy->getScanID());
02214 }
02215 }
02216 #endif
02217
02218 copy->setScanMode(dir->getScanMode());
02219
02220 if (dir->getScanMode() == TimedScanMode)
02221 {
02222 scanID = autoscan_timed->add(copy);
02223 timerNotify(copy->getTimerParameter());
02224 }
02225 #ifdef HAVE_INOTIFY
02226 if (ConfigManager::getInstance()->getBoolOption(CFG_IMPORT_AUTOSCAN_USE_INOTIFY))
02227 {
02228 if (dir->getScanMode() == InotifyScanMode)
02229 {
02230 autoscan_inotify->add(copy);
02231 inotify->monitor(copy);
02232 }
02233 }
02234 #endif
02235
02236 storage->updateAutoscanDirectory(copy);
02237 if (original->getScanMode() != copy->getScanMode())
02238 SessionManager::getInstance()->containerChangedUI(copy->getObjectID());
02239 }
02240
02241 #ifdef HAVE_MAGIC
02242 zmm::String ContentManager::getMimeTypeFromBuffer(void *buffer, size_t length)
02243 {
02244 return get_mime_type_from_buffer(ms, reMimetype, buffer, length);
02245 }
02246 #endif
02247
02248 #ifdef YOUTUBE
02249 void ContentManager::checkCachedURLs()
02250 {
02251 AUTOLOCK(urlcache_mutex);
02252
02253 time_t now = time(NULL);
02254
02255 log_debug("Checking cached URLs..stored: %d\n", cached_urls->size());
02256 int count = 0;
02257 int i = 0;
02258
02259 while (count < cached_urls->size())
02260 {
02261 Ref<CachedURL> cached = cached_urls->get(i);
02262 if (cached != nil)
02263 {
02264
02265
02266 if ((cached->getLastAccessTime() + URL_CACHE_LIFETIME) < now)
02267 {
02268 log_debug("URL of object: %d, url: %s exceeds "
02269 "lifetime (%lld < %lld), purging...\n",
02270 cached_urls->get(i)->getObjectID(),
02271 cached_urls->get(i)->getURL().c_str(),
02272 (long long)(cached->getLastAccessTime() +
02273 URL_CACHE_LIFETIME),
02274 (long long)now);
02275 cached_urls->removeUnordered(i);
02276 }
02277 else
02278 i++;
02279 }
02280 count++;
02281 }
02282
02283 log_debug("URL Cache check complete, remaining items: %d\n",
02284 cached_urls->size());
02285
02286 if (cached_urls->size() > 0)
02287 {
02288 Ref<TimerParameter> url_cache_check(new
02289 TimerParameter(TimerParameter::IDURLCache, -1));
02290
02291 Timer::getInstance()->addTimerSubscriber(
02292 AS_TIMER_SUBSCRIBER_SINGLETON(this),
02293 URL_CACHE_CHECK_INTERVAL,
02294 RefCast(url_cache_check, Object), true);
02295 }
02296 }
02297
02298 void ContentManager::cacheURL(zmm::Ref<CachedURL> url)
02299 {
02300 AUTOLOCK(urlcache_mutex);
02301 time_t oldest = time(NULL);
02302 int oldest_index = -1;
02303 bool added = false;
02304 int old_size = cached_urls->size();
02305
02306 log_debug("Request to cache id %d, URL %s\n", url->getObjectID(),
02307 url->getURL().c_str());
02308 for (int i = 0; i < cached_urls->size(); i++)
02309 {
02310 Ref<CachedURL> cached = cached_urls->get(i);
02311 if (cached != nil)
02312 {
02313
02314 if (cached->getLastAccessTime() < oldest)
02315 {
02316 oldest = cached->getLastAccessTime();
02317 oldest_index = i;
02318 }
02319
02320
02321 if (url->getObjectID() == cached->getObjectID())
02322 {
02323 log_debug("Updating cache for object %d\n", url->getObjectID());
02324 cached_urls->set(url, i);
02325 added = true;
02326 break;
02327 }
02328 }
02329 }
02330
02331 if (!added)
02332 {
02333
02334 if (cached_urls->size() >= MAX_CACHED_URLS)
02335 {
02336 log_debug("Checking if we need to remove something old...");
02337 if ((oldest_index > -1) && (oldest_index <= MAX_CACHED_URLS))
02338 {
02339 log_debug("Removing old url from cache of object %d\n",
02340 cached_urls->get(oldest_index)->getObjectID());
02341 cached_urls->removeUnordered(oldest_index);
02342 }
02343 else
02344 {
02345 throw _Exception(_("Index exceeds URL cache size: ") +
02346 String::from(oldest_index));
02347 }
02348 }
02349
02350 log_debug("Appeinding url to the cache: %d\n", url->getObjectID());
02351 cached_urls->append(url);
02352 }
02353
02354 if ((cached_urls->size() > 0) && (old_size == 0))
02355 {
02356 log_debug("URL Cache is not empty, adding invalidation timer!\n");
02357 Ref<TimerParameter> url_cache_check(new
02358 TimerParameter(TimerParameter::IDURLCache, -1));
02359
02360 Timer::getInstance()->addTimerSubscriber(
02361 AS_TIMER_SUBSCRIBER_SINGLETON(this),
02362 URL_CACHE_CHECK_INTERVAL,
02363 RefCast(url_cache_check, Object), true);
02364 }
02365 }
02366
02367 String ContentManager::getCachedURL(int objectID)
02368 {
02369 AUTOLOCK(urlcache_mutex);
02370 log_debug("Asked for an url from cache...\n");
02371 for (int i = 0; i < cached_urls->size(); i++)
02372 {
02373 Ref<CachedURL> cached = cached_urls->get(i);
02374 if (cached != nil)
02375 {
02376 if (cached->getObjectID() == objectID)
02377 {
02378 log_debug("Found URL in cache for object ID %d, URL: %s\n",
02379 objectID, cached->getURL().c_str());
02380 return cached->getURL();
02381 }
02382 }
02383 }
02384 return nil;
02385 }
02386 #endif
02387
02388 CMAddFileTask::CMAddFileTask(String path, String rootpath, bool recursive, bool hidden, bool cancellable) : GenericTask(ContentManagerTask)
02389 {
02390 this->path = path;
02391 this->rootpath = rootpath;
02392 this->recursive = recursive;
02393 this->hidden = hidden;
02394 this->taskType = AddFile;
02395 this->cancellable = cancellable;
02396 }
02397
02398 String CMAddFileTask::getPath()
02399 {
02400 return path;
02401 }
02402
02403 String CMAddFileTask::getRootPath()
02404 {
02405 return rootpath;
02406 }
02407 void CMAddFileTask::run()
02408 {
02409 log_debug("running add file task with path %s recursive: %d\n", path.c_str(), recursive);
02410 Ref<ContentManager> cm = ContentManager::getInstance();
02411 cm->_addFile(path, nil, recursive, hidden, Ref<GenericTask> (this));
02412 }
02413
02414 CMRemoveObjectTask::CMRemoveObjectTask(int objectID, bool all) : GenericTask(ContentManagerTask)
02415 {
02416 this->objectID = objectID;
02417 this->all = all;
02418 this->taskType = RemoveObject;
02419 cancellable = false;
02420 }
02421
02422 void CMRemoveObjectTask::run()
02423 {
02424 Ref<ContentManager> cm = ContentManager::getInstance();
02425 cm->_removeObject(objectID, all);
02426 }
02427
02428 CMRescanDirectoryTask::CMRescanDirectoryTask(int objectID, int scanID, scan_mode_t scanMode, bool cancellable) : GenericTask(ContentManagerTask)
02429 {
02430 this->scanID = scanID;
02431 this->scanMode = scanMode;
02432 this->objectID = objectID;
02433 this->taskType = RescanDirectory;
02434 this->cancellable = cancellable;
02435 }
02436
02437 void CMRescanDirectoryTask::run()
02438 {
02439 Ref<ContentManager> cm = ContentManager::getInstance();
02440 Ref<AutoscanDirectory> dir = cm->getAutoscanDirectory(scanID, scanMode);
02441 if (dir == nil)
02442 return;
02443
02444 cm->_rescanDirectory(objectID, dir->getScanID(), dir->getScanMode(), dir->getScanLevel(), Ref<GenericTask> (this));
02445 dir->decTaskCount();
02446
02447 if (dir->getTaskCount() == 0)
02448 {
02449 dir->updateLMT();
02450 if (dir->getScanMode() == TimedScanMode)
02451 Timer::getInstance()->addTimerSubscriber(AS_TIMER_SUBSCRIBER_SINGLETON_FROM_REF(cm), dir->getInterval(), dir->getTimerParameter(), true);
02452 }
02453 }
02454
02455 #ifdef ONLINE_SERVICES
02456 CMFetchOnlineContentTask::CMFetchOnlineContentTask(Ref<OnlineService> service,
02457 Ref<Layout> layout,
02458 bool cancellable,
02459 bool unscheduled_refresh) : GenericTask(ContentManagerTask)
02460 {
02461 this->layout = layout;
02462 this->service = service;
02463 this->taskType = FetchOnlineContent;
02464 this->cancellable = cancellable;
02465 this->unscheduled_refresh = unscheduled_refresh;
02466 }
02467
02468 void CMFetchOnlineContentTask::run()
02469 {
02470 if (this->service == nil)
02471 {
02472 log_debug("Received invalid service!\n");
02473 return;
02474 }
02475 try
02476 {
02477 Ref<GenericTask> t(new TPFetchOnlineContentTask(service, layout,
02478 cancellable,
02479 unscheduled_refresh));
02480 TaskProcessor::getInstance()->addTask(t);
02481 }
02482 catch (Exception ex)
02483 {
02484 log_error("%s\n", ex.getMessage().c_str());
02485 }
02486 }
02487 #endif//ONLINE_SERVICES
02488
02489 CMLoadAccountingTask::CMLoadAccountingTask() : GenericTask(ContentManagerTask)
02490 {
02491 this->taskType = LoadAccounting;
02492 }
02493
02494 void CMLoadAccountingTask::run()
02495 {
02496 Ref<ContentManager> cm = ContentManager::getInstance();
02497 cm->_loadAccounting();
02498 }
02499
02500 CMAccounting::CMAccounting() : Object()
02501 {
02502 totalFiles = 0;
02503 }