PageRenderTime 120ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/src/lib/ContentDirectory/ContentDatabase.cpp

https://github.com/u-voelkel/fuppes
C++ | 1421 lines | 505 code | 213 blank | 703 comment | 99 complexity | f4f0dc3aa0a300210773d76216506fbf MD5 | raw file
  1. /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
  2. /***************************************************************************
  3. * ContentDatabase.cpp
  4. *
  5. * FUPPES - Free UPnP Entertainment Service
  6. *
  7. * Copyright (C) 2005-2010 Ulrich Völkel <u-voelkel@users.sourceforge.net>
  8. ****************************************************************************/
  9. /*
  10. * This program is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License along
  21. * with this program; if not, write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  23. */
  24. #include "ContentDatabase.h"
  25. #include "../ContentDatabase/ContentDatabase.h"
  26. #include "../SharedLog.h"
  27. #include "FileDetails.h"
  28. #include "../Common/RegEx.h"
  29. #include "../Common/Common.h"
  30. #include "../Common/Directory.h"
  31. #include "../Common/File.h"
  32. #include "HotPlug.h"
  33. #include "../Plugins/PluginMgr.h"
  34. //#include "DatabaseObject.h"
  35. #include "../Thread/ThreadPool.h"
  36. #include <sstream>
  37. #include <string>
  38. #include <fstream>
  39. #include <cstdio>
  40. #ifndef WIN32
  41. #include <dirent.h>
  42. #endif
  43. #include <time.h>
  44. #include <sys/types.h>
  45. #include <sys/stat.h>
  46. #include <stdio.h>
  47. //#include <iostream>
  48. #include "../Plugins/ItemSource.h"
  49. using namespace std;
  50. using namespace fuppes;
  51. using namespace Threading;
  52. //static bool g_bIsRebuilding;
  53. /*static bool g_bFullRebuild;
  54. static bool g_bAddNew;
  55. static bool g_bRemoveMissing;
  56. */
  57. object_id_t CContentDatabase::m_objectId = 0;
  58. uint32_t CContentDatabase::m_systemUpdateId = 0;
  59. /*
  60. CContentDatabase* CContentDatabase::m_Instance = 0;
  61. CContentDatabase* CContentDatabase::Shared()
  62. {
  63. if(m_Instance == 0) {
  64. m_Instance = new CContentDatabase();
  65. }
  66. return m_Instance;
  67. }
  68. */
  69. CContentDatabase::CContentDatabase(Fuppes::FuppesConfig& fuppesConfig, CFileDetails& fileDetails, Plugins::CPluginMgr& pluginMgr, VirtualContainer::Manager& virtualContainerMgr):
  70. m_fuppesConfig(fuppesConfig),
  71. m_fileDetails(fileDetails),
  72. m_pluginMgr(pluginMgr),
  73. m_virtualContainerMgr(virtualContainerMgr),
  74. //m_updateThread(this, fuppesConfig, fileDetails, pluginMgr, virtualContainerMgr, m_fileAlterationHandler),
  75. m_fileAlterationHandler(this)
  76. {
  77. m_rebuildThread = NULL;
  78. m_objectId = 0;
  79. m_systemUpdateId = 0;
  80. //m_fileAlterationHandler = new FileAlterationHandler();
  81. m_pFileAlterationMonitor = CFileAlterationMgr::CreateMonitor(&m_fileAlterationHandler);
  82. m_fileAlterationHandler.setMonitor(m_pFileAlterationMonitor);
  83. //m_updateThread = new UpdateThread(&m_fileAlterationHandler);
  84. m_fileAlterationMonitor = NULL;
  85. //HotPlugMgr::init();
  86. }
  87. CContentDatabase::~CContentDatabase()
  88. {
  89. //HotPlugMgr::uninit();
  90. if (NULL != m_fileAlterationMonitor) {
  91. m_fileAlterationMonitor->stop();
  92. m_fileAlterationMonitor->close();
  93. delete m_fileAlterationMonitor;
  94. }
  95. //delete m_updateThread;
  96. delete m_pFileAlterationMonitor;
  97. //delete m_fileAlterationHandler;
  98. if(m_rebuildThread != NULL) {
  99. delete m_rebuildThread;
  100. m_rebuildThread = NULL;
  101. }
  102. }
  103. bool CContentDatabase::Init()
  104. {
  105. //return true;
  106. //SQLQuery qry;
  107. /* string sql = qry.build(SQL_TABLES_EXIST, 0);
  108. qry.select(sql);
  109. if(qry.eof()) {
  110. *p_bIsNewDB = true;
  111. // create tables
  112. sql = qry.build(SQL_CREATE_TABLE_DB_INFO, 0);
  113. qry.exec(sql);
  114. sql = qry.build(SQL_CREATE_TABLE_OBJECTS, 0);
  115. qry.exec(sql);
  116. sql = qry.build(SQL_CREATE_TABLE_OBJECT_DETAILS, 0);
  117. qry.exec(sql);
  118. */
  119. // create indices
  120. //#warning "todo"
  121. /*StringList indices = String::split(qry.connection()->getStatement(SQL_CREATE_INDICES), ";");
  122. for(unsigned int i = 0; i < indices.size(); i++) {
  123. qry.exec(indices.at(i));
  124. }*/
  125. /*
  126. // set db version
  127. sql = qry.build(SQL_SET_DB_INFO, DB_VERSION);
  128. qry.exec(sql);
  129. }
  130. */
  131. // this is mysql only code
  132. /*
  133. qry.select("SHOW VARIABLES LIKE 'char%'");
  134. while(!qry.eof()) {
  135. cout << qry.result()->asString("Variable_name") << " :: " << qry.result()->asString("Value") << endl;
  136. qry.next();
  137. }
  138. */
  139. /*
  140. qry.select("select VERSION from FUPPES_DB_INFO");
  141. if(qry.eof()) {
  142. CSharedLog::Shared()->UserError("no database version information found. remove the fuppes.db and restart fuppes");
  143. return false;
  144. }
  145. if(qry.result()->asInt("VERSION") != DB_VERSION) {
  146. CSharedLog::Shared()->UserError("database version mismatch. remove the fuppes.db and restart fuppes");
  147. return false;
  148. }
  149. */
  150. m_fileAlterationMonitor = NULL; //Fam::Monitor::createMonitor(*this, true); // m_fuppesConfig.databaseSettings.readonly);
  151. if(true == m_fuppesConfig.databaseSettings.readonly) {
  152. return true;
  153. }
  154. // get max object id
  155. Database::ObjectManager objMgr;
  156. objMgr.getMaxObjectId(CContentDatabase::m_objectId);
  157. // setup file alteration monitor
  158. //if(m_pFileAlterationMonitor->isActive()) {
  159. Database::QueryResult result;
  160. objMgr.getAllContainerPaths(result);
  161. for(size_t i = 0; i < result.size(); i++) {
  162. // m_pFileAlterationMonitor->addWatch(result.getRow(i)->getColumnString("PATH"));
  163. //m_fileAlterationMonitor->addWatch(result.getRow(i)->getColumnString("PATH"), true);
  164. }
  165. // }
  166. //m_fileAlterationMonitor->start();
  167. //m_updateThread.start();
  168. /*
  169. SQLQuery qry;
  170. // get max object id
  171. qry.select("select max(OBJECT_ID) as VALUE from OBJECTS where DEVICE is NULL");
  172. if(!qry.eof()) {
  173. CContentDatabase::m_objectId = qry.result()->asUInt("VALUE");
  174. }
  175. // setup file alteration monitor
  176. if(m_pFileAlterationMonitor->isActive()) {
  177. qry.select("select PATH from OBJECTS where TYPE >= 1 and TYPE < 100 and DEVICE is NULL");
  178. while (!qry.eof()) {
  179. m_pFileAlterationMonitor->addWatch(qry.result()->asString("PATH"));
  180. qry.next();
  181. }
  182. }
  183. */
  184. // start update thread
  185. //if(!*p_bIsNewDB)
  186. // m_updateThread.start();
  187. return true;
  188. }
  189. uint32_t CContentDatabase::getSystemUpdateId() // static
  190. {
  191. return CContentDatabase::m_systemUpdateId;
  192. }
  193. void CContentDatabase::incSystemUpdateId() // static
  194. {
  195. CContentDatabase::m_systemUpdateId++;
  196. // todo: execute "Service Reset Procedure" if m_systemUpdateId reaches it's max value
  197. }
  198. void CContentDatabase::startUpdateThread()
  199. {
  200. /*if(!m_updateThread.running()) {
  201. if(m_updateThread.finished()) {
  202. m_updateThread.close();
  203. }
  204. m_updateThread.start();
  205. }*/
  206. }
  207. /*
  208. void CContentDatabase::onFamEvent(Fam::Event& event)
  209. {
  210. std::cout << "CContentDatabase::onFamEvent" << std::endl;
  211. }
  212. void CContentDatabase::onDirectoryCreateEvent(const std::string path, const std::string name)
  213. {
  214. std::cout << "CContentDatabase::onDirectoryCreateEvent" << std::endl;
  215. Database::Item parent;
  216. Database::ObjectManager objMgr;
  217. if (!objMgr.findOneByFilename(path, "", parent, false)) {
  218. return;
  219. }
  220. // if the parent is already marked as modified we can ignore this event
  221. if (parent.modified > parent.updated) {
  222. return;
  223. }
  224. Database::Item dir;
  225. dir.objectId = CContentDatabase::GetObjId();
  226. dir.parentId = parent.objectId;
  227. dir.type = CONTAINER_STORAGEFOLDER;
  228. dir.path = path + name + "/";
  229. dir.title = name;
  230. dir.modified = DateTime::now().toInt();
  231. dir.updated = 0;
  232. objMgr.persist(dir);
  233. m_fileAlterationMonitor->addWatch(dir.path);
  234. }
  235. void CContentDatabase::onDirectoryRemoveEvent(Fam::Event& event)
  236. {
  237. std::cout << "ON EVENT" << std::endl;
  238. }
  239. void CContentDatabase::onDirectoryMoveEvent(Fam::Event& event)
  240. {
  241. std::cout << "ON EVENT" << std::endl;
  242. }
  243. */
  244. void CContentDatabase::moveDirectory(std::string oldpath, std::string newparent, std::string newdir) // static
  245. {
  246. #warning todo
  247. /*
  248. DbObject* obj = DbObject::createFromFileName(oldpath);
  249. if(!obj) {
  250. cout << "CContentDatabase error: directory: " << oldpath << " not found 1" << endl;
  251. return;
  252. }
  253. // get the new parent
  254. DbObject* parent = DbObject::createFromFileName(newparent);
  255. if(!parent) {
  256. cout << "CContentDatabase directory: " << newparent << " not found" << endl;
  257. delete obj;
  258. return;
  259. }
  260. string newPath = Directory::appendTrailingSlash(newparent + newdir);
  261. obj->setPath(newPath);
  262. obj->setTitle(newdir);
  263. obj->setParentId(parent->objectId());
  264. obj->save();
  265. delete obj;
  266. delete parent;
  267. */
  268. // increment systemUpdateId
  269. CContentDatabase::incSystemUpdateId();
  270. }
  271. void CContentDatabase::removeDirectory(std::string path) // static
  272. {
  273. #warning todo
  274. /*
  275. DbObject* dir = DbObject::createFromFileName(path);
  276. if(!dir) {
  277. cout << "CContentDatabase: directory: " << path << " not found 2" << endl;
  278. return;
  279. }
  280. // remove all virtual files that are build from the directories content
  281. //m_virtualContainerMgr.deleteDirectory(dir);
  282. // remove the dir
  283. dir->remove();
  284. delete dir;
  285. */
  286. // increment systemUpdateId
  287. CContentDatabase::incSystemUpdateId();
  288. }
  289. /*
  290. void BuildPlaylists();
  291. void ParsePlaylist(CSQLResult* pResult);
  292. void ParseM3UPlaylist(CSQLResult* pResult);
  293. void ParsePLSPlaylist(CSQLResult* pResult);
  294. */
  295. std::string findAlbumArtFile(std::string dir, CFileDetails* fileDetails);
  296. //unsigned int InsertFile(CContentDatabase* pDb, SQLQuery* qry, unsigned int p_nParentId, std::string p_sFileName, bool hidden = false);
  297. object_id_t GetObjectIDFromFileName(Database::ObjectManager& objMgr, std::string p_sFileName);
  298. void CContentDatabase::BuildDB(int rebuildType, int delay /*= 0*/)
  299. {
  300. if(IsRebuilding())
  301. return;
  302. if(m_rebuildThread) {
  303. delete m_rebuildThread;
  304. m_rebuildThread = NULL;
  305. }
  306. // m_updateThread.stop();
  307. m_rebuildThread = new RebuildThread(this, delay);
  308. m_rebuildThread->setRebuildType(rebuildType);
  309. m_rebuildThread->start();
  310. }
  311. void CContentDatabase::RebuildDB(int delay /*= 0*/)
  312. {
  313. if(IsRebuilding() || m_fuppesConfig.databaseSettings.readonly)
  314. return;
  315. BuildDB(RebuildThread::rebuild, delay);
  316. }
  317. void CContentDatabase::UpdateDB()
  318. {
  319. if(IsRebuilding() || m_fuppesConfig.databaseSettings.readonly)
  320. return;
  321. /*SQLQuery qry; // = CDatabase::query();
  322. qry.select("select max(OBJECT_ID) as VALUE from OBJECTS where DEVICE is NULL");
  323. if(!qry.eof()) {
  324. CContentDatabase::m_objectId = qry.result()->asUInt("VALUE");
  325. }*/
  326. //delete qry;
  327. BuildDB(RebuildThread::addNew | RebuildThread::removeMissing);
  328. }
  329. void CContentDatabase::AddNew()
  330. {
  331. if(IsRebuilding() || m_fuppesConfig.databaseSettings.readonly)
  332. return;
  333. /*SQLQuery qry; // = CDatabase::query();
  334. qry.select("select max(OBJECT_ID) as VALUE from OBJECTS where DEVICE is NULL");
  335. if(!qry.eof()) {
  336. CContentDatabase::m_objectId = qry.result()->asUInt("VALUE");
  337. }*/
  338. //delete qry;
  339. BuildDB(RebuildThread::addNew);
  340. }
  341. void CContentDatabase::RemoveMissing()
  342. {
  343. if(IsRebuilding() || m_fuppesConfig.databaseSettings.readonly)
  344. return;
  345. BuildDB(RebuildThread::removeMissing);
  346. }
  347. bool CContentDatabase::IsRebuilding()
  348. {
  349. return (m_rebuildThread && m_rebuildThread->running());
  350. }
  351. object_id_t CContentDatabase::GetObjId() // static
  352. {
  353. return ++CContentDatabase::m_objectId;
  354. }
  355. object_id_t CContentDatabase::insertFile(std::string fileName, object_id_t parentId /*= 0*/, Database::ObjectManager& objMgr /*= NULL*/, bool lock /*= false*/)
  356. {
  357. if(lock) {
  358. MutexLocker locker(&m_insertMutex);
  359. return insert_file_helper(fileName, parentId, objMgr);
  360. } else {
  361. return insert_file_helper(fileName, parentId, objMgr);
  362. }
  363. }
  364. object_id_t CContentDatabase::insert_file_helper(
  365. std::string fileName,
  366. object_id_t parentId,
  367. Database::ObjectManager& objMgr)
  368. {
  369. // split path and filename
  370. string path = File(fileName).path();
  371. fileName = fileName.substr(path.length(), fileName.length() - path.length());
  372. Database::Item item;
  373. objMgr.findOneByFilename(path, fileName, item, false);
  374. //DbObject* file = DbObject::createFromFileName(fileName, qry);
  375. if(0 < item.objectId) {
  376. return item.objectId;
  377. //object_id_t result = file->objectId();
  378. //delete file;
  379. //return result;
  380. }
  381. // get the object type
  382. OBJECT_TYPE objectType = m_fileDetails.GetObjectType(fileName);
  383. if(objectType == OBJECT_TYPE_UNKNOWN) {
  384. std::cout << "unknown object type: " << fileName << std::endl;
  385. return 0;
  386. }
  387. bool visible = !m_fileDetails.isAlbumArtFile(fileName);
  388. // get the parent object
  389. if(parentId == 0) {
  390. Database::Item parent;
  391. //DbObject* parent = DbObject::createFromFileName(path, qry);
  392. objMgr.findOneByFilename(path, "", parent, false);
  393. if(0 == parent.objectId) {
  394. cout << "CContentDatabase error: directory: " << path << " not found 3 for file: " << fileName << endl;
  395. assert(true == false);
  396. return 0;
  397. }
  398. parentId = parent.objectId; //parent->objectId();
  399. //delete parent;
  400. }
  401. // format title
  402. string title = fuppes::FormatHelper::fileNameToTitle(fileName, m_fuppesConfig.globalSettings.localCharset);
  403. // create the object
  404. item.objectId = CContentDatabase::GetObjId();
  405. item.parentId = parentId;
  406. item.type = objectType;
  407. item.path = path;
  408. item.filename = fileName;
  409. item.extension = ExtractFileExt(fileName);
  410. item.title = title;
  411. item.visible = visible;
  412. item.size = 0; //getFileSize(path + fileName);
  413. objMgr.persist(item);
  414. /*
  415. DbObject obj;
  416. obj.setParentId(parentId);
  417. obj.setType(objectType);
  418. obj.setPath(path);
  419. obj.setFileName(fileName);
  420. obj.setTitle(title);
  421. obj.setVisible(visible);
  422. obj.save(qry);
  423. */
  424. return item.objectId;
  425. }
  426. object_id_t CContentDatabase::insertDirectory(std::string path, std::string title, object_id_t parentId, Database::ObjectManager& objMgr /*= NULL*/, bool lock /*= false*/)
  427. {
  428. if(lock) {
  429. MutexLocker locker(&m_insertMutex);
  430. return insert_directory_helper(path, title, parentId, objMgr);
  431. } else {
  432. return insert_directory_helper(path, title, parentId, objMgr);
  433. }
  434. }
  435. object_id_t CContentDatabase::insert_directory_helper(
  436. std::string directory_path,
  437. std::string title,
  438. object_id_t parent_id,
  439. Database::ObjectManager& objMgr)
  440. {
  441. Database::Item dir;
  442. objMgr.findOneByFilename(directory_path, "", dir, false);
  443. //DbObject* dir = DbObject::createFromFileName(path, qry);
  444. if(0 == dir.objectId) {
  445. dir.objectId = CContentDatabase::GetObjId();
  446. dir.parentId = parent_id;
  447. dir.type = CONTAINER_STORAGEFOLDER;
  448. dir.path = directory_path;
  449. dir.title = title;
  450. objMgr.persist(dir);
  451. /*
  452. dir = new DbObject();
  453. dir->setParentId(parent_id);
  454. dir->setType(CONTAINER_STORAGEFOLDER);
  455. dir->setPath(path);
  456. dir->setTitle(title);
  457. dir->save(qry);
  458. */
  459. m_pFileAlterationMonitor->addWatch(directory_path);
  460. }
  461. return dir.objectId;
  462. }
  463. void CContentDatabase::addSharedObject(Configuration::SharedObject* so)
  464. {
  465. }
  466. void CContentDatabase::removeSharedObject(Configuration::SharedObject* so)
  467. {
  468. }
  469. void CContentDatabase::scanDirectory(std::string path)
  470. {
  471. ScanDirectoryThread* thread = new ScanDirectoryThread(this, path);
  472. Threading::ThreadPool::deleteLater(thread);
  473. thread->start();
  474. /*std::list<ScanDirectoryThread*>::iterator iter;
  475. for(iter = m_Instance->m_scanDirThreadList.begin();
  476. iter != m_Instance->m_scanDirThreadList.end();
  477. ) {
  478. if((*iter)->finished()) {
  479. iter = m_Instance->m_scanDirThreadList.erase(iter);
  480. delete *iter;
  481. }
  482. else {
  483. iter++;
  484. }
  485. }
  486. m_Instance->m_scanDirThreadList.push_back(thread);
  487. */
  488. }
  489. void ScanDirectoryThread::run()
  490. {
  491. Database::ObjectManager objMgr;
  492. Database::Item parent;
  493. objMgr.findOneByFilename(m_path, "", parent, false);
  494. //DbObject* parent = DbObject::createFromFileName(m_path);
  495. //SQLQuery qry;
  496. scanDir(objMgr, m_path, parent.objectId);
  497. //delete parent;
  498. }
  499. void ScanDirectoryThread::scanDir(Database::ObjectManager& objMgr, std::string path, unsigned int parentId)
  500. {
  501. Directory dir(path);
  502. dir.open();
  503. DirEntryList entries = dir.dirEntryList();
  504. dir.close();
  505. DirEntry entry;
  506. unsigned int objectId = 0;
  507. for(unsigned int i = 0; i < entries.size(); i++) {
  508. entry = entries.at(i);
  509. if(entry.type() == DirEntry::Directory) {
  510. //CContentDatabase::insertFile(fileName, qry);
  511. objectId = m_contentDb->insertDirectory(entry.absolutePath(), entry.name(), parentId, objMgr, true);
  512. scanDir(objMgr, entry.absolutePath(), objectId);
  513. }
  514. else if(entry.type() == DirEntry::File) {
  515. m_contentDb->insertFile(entry.absolutePath(), parentId, objMgr, true);
  516. }
  517. }
  518. }
  519. void RebuildThread::DbScanDir(CContentDatabase* db, Database::ObjectManager& objMgr, std::string p_sDirectory, object_id_t p_nParentId)
  520. {
  521. p_sDirectory = Directory::appendTrailingSlash(p_sDirectory);
  522. if(!Directory::exists(p_sDirectory))
  523. return;
  524. log(Logging::Log::contentdb, Logging::Log::extended)<< "read dir \"" << p_sDirectory << "\"";
  525. Directory dir(p_sDirectory);
  526. dir.open();
  527. DirEntryList list = dir.dirEntryList();
  528. dir.close();
  529. // DbObject obj;
  530. Database::Item item;
  531. object_id_t objectId = 0;
  532. for(unsigned int i = 0; i < list.size(); i++) {
  533. DirEntry entry = list.at(i);
  534. objectId = 0;
  535. if(entry.type() == DirEntry::Directory && !Directory::hidden(entry.path())) {
  536. if(m_rebuildType & RebuildThread::addNew) {
  537. objectId = GetObjectIDFromFileName(objMgr, entry.path());
  538. }
  539. if(0 == objectId) {
  540. objectId = db->GetObjId();
  541. OBJECT_TYPE folderType = CONTAINER_STORAGEFOLDER;
  542. // insert the directory
  543. /*obj.reset();
  544. obj.setObjectId(objectId);
  545. obj.setParentId(p_nParentId);
  546. obj.setType(folderType);
  547. obj.setPath(entry.path());
  548. obj.setTitle(ToUTF8(entry.name(), db->m_fuppesConfig.globalSettings.localCharset));
  549. obj.save(qry);*/
  550. item.clear();
  551. item.objectId = objectId;
  552. item.parentId = p_nParentId;
  553. item.type = folderType;
  554. item.path = entry.path();
  555. item.title = ToUTF8(entry.name(), db->m_fuppesConfig.globalSettings.localCharset);
  556. objMgr.persist(item);
  557. // watch the dir
  558. db->fileAlterationMonitor()->addWatch(entry.absolutePath());
  559. // check for album art
  560. string albumArt = findAlbumArtFile(entry.path(), db->getFileDetails());
  561. if(albumArt.length() > 0) {
  562. object_id_t artId = GetObjectIDFromFileName(objMgr, albumArt);
  563. folderType = CONTAINER_ALBUM_MUSICALBUM;
  564. if(artId == 0) {
  565. InsertFile(db, objMgr, objectId, albumArt, true);
  566. }
  567. }
  568. }
  569. // recursively scan subdirectories
  570. DbScanDir(db, objMgr, entry.absolutePath(), objectId);
  571. }
  572. else if(entry.type() == DirEntry::File) {
  573. string sExt = ExtractFileExt(entry.name());
  574. if(m_contentDb->getFileDetails()->IsSupportedFileExtension(sExt)) {
  575. if(m_rebuildType & RebuildThread::addNew) {
  576. objectId = GetObjectIDFromFileName(objMgr, entry.absolutePath());
  577. }
  578. if(objectId == 0) {
  579. InsertFile(db, objMgr, p_nParentId, entry.absolutePath());
  580. // msleep(1);
  581. }
  582. }
  583. } // file
  584. } // for
  585. }
  586. std::string findAlbumArtFile(std::string dir, CFileDetails* fileDetails)
  587. {
  588. string file;
  589. dir = Directory::appendTrailingSlash(dir);
  590. string result;
  591. #ifdef WIN32
  592. WIN32_FIND_DATA data;
  593. HANDLE hFile = FindFirstFile(string(dir + "*").c_str(), &data);
  594. if(NULL == hFile)
  595. return "";
  596. while(FindNextFile(hFile, &data)) {
  597. if(((string(".").compare(data.cFileName) != 0) &&
  598. (string("..").compare(data.cFileName) != 0))) {
  599. file = data.cFileName;
  600. #else
  601. DIR* pDir;
  602. dirent* pDirEnt;
  603. if((pDir = opendir(dir.c_str())) != NULL) {
  604. while ((pDirEnt = readdir(pDir)) != NULL) {
  605. if(((string(".").compare(pDirEnt->d_name) != 0) && (string("..").compare(pDirEnt->d_name) != 0))) {
  606. file = pDirEnt->d_name;
  607. #endif
  608. if(fileDetails->isAlbumArtFile(file)) {
  609. result = dir + file;
  610. break;
  611. }
  612. } // if
  613. } // while FindNext | readdir
  614. #ifndef WIN32
  615. closedir(pDir);
  616. } /* if opendir */
  617. #endif
  618. return result;
  619. }
  620. unsigned int findAlbumArt(CFileDetails* fileDetails, std::string dir, std::string* ext/*, SQLQuery* qry*/)
  621. {
  622. string file = findAlbumArtFile(dir, fileDetails);
  623. if(file.length() == 0) {
  624. return 0;
  625. }
  626. string path = File(file).path();
  627. file = file.substr(path.length(), file.length());
  628. #warning todo
  629. /*
  630. qry->select("select * from OBJECTS "
  631. "where PATH = '" + SQLEscape(path) + "' and "
  632. "FILE_NAME = '" + SQLEscape(file) + "' and "
  633. "DEVICE is NULL");
  634. if(qry->eof()) {
  635. return 0;
  636. }
  637. *ext = ExtractFileExt(file);
  638. return qry->result()->asUInt("OBJECT_ID");
  639. */
  640. return 0;
  641. }
  642. object_id_t RebuildThread::InsertFile(CContentDatabase* pDb, Database::ObjectManager& objMgr, object_id_t p_nParentId, std::string p_sFileName, bool hidden /* = false*/)
  643. {
  644. object_id_t nObjId = 0;
  645. if(m_rebuildType & RebuildThread::addNew) {
  646. nObjId = GetObjectIDFromFileName(objMgr, p_sFileName);
  647. if(nObjId > 0) {
  648. return nObjId;
  649. }
  650. }
  651. return pDb->insertFile(p_sFileName, p_nParentId, objMgr);
  652. }
  653. unsigned int InsertURL(std::string p_sURL, std::string p_sTitle = "", std::string p_sMimeType = "")
  654. {
  655. // todo FIXME: object type
  656. OBJECT_TYPE nObjectType = ITEM_AUDIO_ITEM_AUDIO_BROADCAST;
  657. unsigned int nObjId = CContentDatabase::GetObjId();
  658. //SQLQuery qry;
  659. stringstream sSql;
  660. sSql << "insert into OBJECTS (TYPE, OBJECT_ID, PATH, FILE_NAME, TITLE, MIME_TYPE) values " << "(" << nObjectType << ", " << nObjId << ", " << "'" << SQLEscape(p_sURL) << "', "
  661. << "'" << SQLEscape(p_sURL) << "', " << "'" << SQLEscape(p_sTitle) << "', " << "'" << SQLEscape(p_sMimeType) << "');";
  662. #warning todo
  663. //qry.exec(sSql.str());
  664. return nObjId;
  665. }
  666. /*
  667. void BuildPlaylists()
  668. {
  669. stringstream sGetPlaylists;
  670. sGetPlaylists <<
  671. "select " <<
  672. " * " <<
  673. "from " <<
  674. " OBJECTS " <<
  675. "where " <<
  676. " TYPE = " << CONTAINER_PLAYLIST_CONTAINER;
  677. SQLQuery qry;
  678. qry.select(sGetPlaylists.str());
  679. while(!qry.eof()) {
  680. ParsePlaylist(qry.result());
  681. qry.next();
  682. }
  683. }
  684. */
  685. object_id_t GetObjectIDFromFileName(Database::ObjectManager& objMgr, std::string p_sFileName)
  686. {
  687. //unsigned int nResult = 0;
  688. //stringstream sSQL;
  689. string path = File(p_sFileName).path();
  690. string fileName;
  691. if(path.length() < p_sFileName.length()) {
  692. fileName = p_sFileName.substr(path.length(), p_sFileName.length());
  693. }
  694. Database::Item item;
  695. if(false == objMgr.findOneByFilename(path, fileName, item, false)) {
  696. return 0;
  697. }
  698. return item.objectId;
  699. //cout << "GetObjectIDFromFileName() : " << p_sFileName << " " << path << " " << fileName << endl;
  700. /*
  701. sSQL << "select OBJECT_ID "
  702. "from OBJECTS "
  703. "where "
  704. " REF_ID = 0 and "
  705. " PATH = '" << SQLEscape(path) << "' ";
  706. if(fileName.empty())
  707. sSQL << " and FILE_NAME is NULL ";
  708. else
  709. sSQL << " and FILE_NAME = '" + SQLEscape(fileName) + "' ";
  710. sSQL << "and DEVICE is NULL";
  711. qry->select(sSQL.str());
  712. if(!qry->eof())
  713. nResult = qry->result()->asUInt("OBJECT_ID");
  714. return nResult;
  715. */
  716. }
  717. /*
  718. void ParsePlaylist(CSQLResult* pResult)
  719. {
  720. BasePlaylistParser* Parser = BasePlaylistParser::Load(pResult->asString("PATH") + pResult->asString("FILE_NAME"));
  721. if(Parser == NULL) {
  722. return;
  723. }
  724. unsigned int nPlaylistID = pResult->asUInt("OBJECT_ID");
  725. unsigned int nObjectID = 0;
  726. //cout << "playlist id: " << nPlaylistID << endl;
  727. CContentDatabase* pDb = CContentDatabase::Shared(); //new CContentDatabase();
  728. SQLQuery qry;
  729. DbObject* existing;
  730. while(!Parser->Eof()) {
  731. if(Parser->Entry()->bIsLocalFile && File::exists(Parser->Entry()->sFileName)) {
  732. // nObjectID = GetObjectIDFromFileName(&qry, Parser->Entry()->sFileName);
  733. existing = DbObject::createFromFileName(Parser->Entry()->sFileName, &qry);
  734. //if(nObjectID == 0) {
  735. if(existing == NULL) {
  736. //nObjectID = InsertFile(pDb, &qry, nPlaylistID, Parser->Entry()->sFileName);
  737. }
  738. else {
  739. //MapPlaylistItem(nPlaylistID, nObjectID);
  740. DbObject* object = new DbObject(existing);
  741. object->setObjectId(pDb->GetObjId());
  742. object->setParentId(nPlaylistID);
  743. object->setRefId(existing->objectId());
  744. object->save(&qry);
  745. delete object;
  746. delete existing;
  747. }
  748. }
  749. else if(!Parser->Entry()->bIsLocalFile) {
  750. nObjectID = InsertURL(Parser->Entry()->sFileName, Parser->Entry()->sTitle, Parser->Entry()->sMimeType);
  751. //MapPlaylistItem(nPlaylistID, nObjectID);
  752. }
  753. Parser->Next();
  754. }
  755. delete Parser;
  756. //delete pDb;
  757. }
  758. */
  759. //fuppesThreadCallback BuildLoop(void* arg)
  760. void RebuildThread::run()
  761. {
  762. if(m_contentDb->getFuppesConfig()->databaseSettings.readonly) {
  763. CSharedLog::Print("[ContentDatabase] readonly database rebuild disabled");
  764. return;
  765. }
  766. if(m_delay > 0)
  767. msleep(m_delay);
  768. // stop update thread
  769. //m_updateThread->stop();
  770. DateTime start = DateTime::now();
  771. CSharedLog::Print("[ContentDatabase] create database at %s", start.toString().c_str());
  772. //SQLQuery qry;
  773. stringstream sSql;
  774. if(m_rebuildType & RebuildThread::rebuild) {
  775. //qry.exec("delete from OBJECTS");
  776. //qry.exec("delete from OBJECT_DETAILS");
  777. CContentDatabase::m_objectId = 0;
  778. //qry.exec("delete from MAP_OBJECTS");
  779. }
  780. Database::ObjectManager objMgr;
  781. /*pDb->Execute("drop index IDX_OBJECTS_OBJECT_ID");
  782. pDb->Execute("drop index IDX_MAP_OBJECTS_OBJECT_ID");
  783. pDb->Execute("drop index IDX_MAP_OBJECTS_PARENT_ID");
  784. pDb->Execute("drop index IDX_OBJECTS_DETAIL_ID");
  785. pDb->Execute("drop index IDX_OBJECT_DETAILS_ID");*/
  786. if(m_rebuildType & RebuildThread::removeMissing) {
  787. CSharedLog::Print("remove missing");
  788. //CContentDatabase* pDel = new CContentDatabase();
  789. //SQLQuery del; // = CDatabase::query();
  790. /*qry.select("select * from OBJECTS");
  791. while (!qry.eof()) {
  792. CSQLResult* result = qry.result();
  793. if(result->asUInt("TYPE") < CONTAINER_MAX) {
  794. if(Directory::exists(result->asString("PATH")) || result->asString("PATH").substr(0, 1).compare("*") == 0) {
  795. qry.next();
  796. continue;
  797. }
  798. }
  799. else {
  800. if(result->asUInt("TYPE") == ITEM_AUDIO_ITEM_AUDIO_BROADCAST || result->asUInt("TYPE") == ITEM_VIDEO_ITEM_VIDEO_BROADCAST
  801. || result->asUInt("TYPE") == ITEM_UNKNOWN_BROADCAST) {
  802. // todo check stream sources availability
  803. qry.next();
  804. continue;
  805. }
  806. if(File::exists(result->asString("PATH") + result->asString("FILE_NAME"))) {
  807. qry.next();
  808. continue;
  809. }
  810. }
  811. DbObject* file = new DbObject(result);
  812. m_contentDb->getVirtualContainerMgr()->deleteFile(file);
  813. delete file;
  814. sSql << "delete from OBJECT_DETAILS where ID = " << result->asString("OBJECT_ID");
  815. del.exec(sSql.str());
  816. sSql.str("");
  817. sSql << "delete from OBJECTS where OBJECT_ID = " << result->asString("OBJECT_ID");
  818. del.exec(sSql.str());
  819. sSql.str("");
  820. qry.next();
  821. }
  822. */
  823. //delete del;
  824. CSharedLog::Print("[DONE] remove missing");
  825. }
  826. #warning TODO
  827. // qry.connection()->vacuum();
  828. int i;
  829. object_id_t nObjId = 0;
  830. string sFileName;
  831. bool bInsert = true;
  832. //DbObject obj;
  833. Database::Item item;
  834. CSharedLog::Print("read shared directories");
  835. CContentDatabase* db = m_contentDb; //CContentDatabase::Shared();
  836. //SharedObjects* sos = CSharedConfig::Shared()->sharedObjects();
  837. Configuration::SharedObjects* sos = &db->getFuppesConfig()->sharedObjects;
  838. Configuration::SharedObject* so;
  839. for(i = 0; i < sos->count(); i++) {
  840. so = sos->getSharedObject(i);
  841. if(so->type != Configuration::SharedObject::directory)
  842. continue;
  843. string tempSharedDir = so->path;
  844. if(Directory::exists(tempSharedDir)) {
  845. db->fileAlterationMonitor()->addWatch(tempSharedDir);
  846. ExtractFolderFromPath(tempSharedDir, &sFileName);
  847. bInsert = true;
  848. if(m_rebuildType & RebuildThread::addNew) {
  849. if((nObjId = GetObjectIDFromFileName(objMgr, tempSharedDir)) > 0) {
  850. bInsert = false;
  851. }
  852. }
  853. sSql.str("");
  854. if(bInsert) {
  855. nObjId = db->GetObjId();
  856. sFileName = ToUTF8(sFileName, m_contentDb->m_fuppesConfig.globalSettings.localCharset);
  857. item.clear();
  858. item.objectId = nObjId;
  859. item.parentId = 0;
  860. item.type = CONTAINER_STORAGEFOLDER;
  861. item.path = tempSharedDir;
  862. item.title = sFileName;
  863. objMgr.persist(item);
  864. /*obj.reset();
  865. obj.setObjectId(nObjId);
  866. obj.setParentId(0);
  867. obj.setType(CONTAINER_STORAGEFOLDER);
  868. obj.setPath(tempSharedDir);
  869. obj.setTitle(sFileName);
  870. obj.save(&qry);*/
  871. }
  872. DbScanDir(db, objMgr, tempSharedDir, nObjId);
  873. }
  874. else {
  875. CSharedLog::Log(L_EXT, __FILE__, __LINE__, "shared directory: \" %s \" not found", tempSharedDir.c_str());
  876. }
  877. } // for
  878. CSharedLog::Print("[DONE] read shared directories");
  879. /*CSharedLog::Print("parse playlists");
  880. BuildPlaylists();
  881. CSharedLog::Print("[DONE] parse playlists");*/
  882. // import iTunes db
  883. /*CSharedLog::Print("parse iTunes databases");
  884. CiTunesImporter* pITunes = new CiTunesImporter();
  885. for(i = 0; i < sos->sharedObjectCount(); i++) {
  886. so = sos->sharedObject(i);
  887. if(so->type() != SharedObject::itunes)
  888. continue;
  889. pITunes->Import(so->path());
  890. }
  891. delete pITunes;
  892. CSharedLog::Print("[DONE] parse iTunes databases");*/
  893. // import other item sources
  894. CSharedLog::Print("update other");
  895. for(i = 0; i < sos->count(); i++) {
  896. so = sos->getSharedObject(i);
  897. if(so->type != Configuration::SharedObject::other)
  898. continue;
  899. //db->updateOther(so);
  900. }
  901. CSharedLog::Print("[DONE] updating other");
  902. // create virtual folder layout
  903. if(m_rebuildType & RebuildThread::rebuild) {
  904. m_contentDb->getVirtualContainerMgr()->rebuildContainerLayouts(false);
  905. //}
  906. //m_contentDb->getVirtualContainerMgr()->getMgr()->RebuildContainerList(true, false);
  907. }
  908. DateTime end = DateTime::now();
  909. CSharedLog::Print("[ContentDatabase] database created at %s", end.toString().c_str());
  910. // start update thread
  911. m_contentDb->startUpdateThread();
  912. }
  913. void CContentDatabase::updateOther(Configuration::SharedObject* so)
  914. {
  915. /*Plugins::ItemSource* plugin = NULL; //CPluginMgr::itemSource(so->otherType());
  916. if(NULL == plugin)
  917. return;
  918. if(!plugin->open(so->path)) {
  919. delete plugin;
  920. return;
  921. }
  922. #warning todo
  923. // get or create the base folder for the source type
  924. DbObject* base = DbObject::createFromFileName("plugin:" + plugin->name());
  925. if(NULL == base) {
  926. base = new DbObject();
  927. base->setPath("*plugin:" + plugin->name());
  928. base->setType(CONTAINER_STORAGEFOLDER);
  929. base->setTitle(plugin->baseName());
  930. base->setParentId(0);
  931. base->save();
  932. }
  933. bool firstImport = false;
  934. // get or create the base folder for the source item
  935. DbObject* source = DbObject::createFromFileName(plugin->name() + ":" + so->path);
  936. if(NULL == source) {
  937. source = new DbObject();
  938. source->setPath("*" + plugin->name() + ":" + so->path);
  939. source->setType(CONTAINER_STORAGEFOLDER);
  940. source->setTitle(so->name);
  941. source->setParentId(base->objectId());
  942. source->save();
  943. firstImport = true;
  944. }
  945. DbObject* last = NULL;
  946. // get the last imported item for the source
  947. if(!firstImport) {
  948. SQLQuery qry;
  949. string sql = ""; //qry.build(SQL_GET_CHILD_OBJECTS, source->objectId()) + "OBJECT_ID desc";
  950. qry.select(sql);
  951. if(!qry.eof()) {
  952. last = new DbObject(qry.result());
  953. }
  954. }
  955. std::list<metadata_t*> items;
  956. metadata_t* metadata;
  957. for(;;) {
  958. metadata = new metadata_t();
  959. init_metadata(metadata);
  960. if(plugin->next(metadata) != 0) {
  961. free_metadata(metadata);
  962. delete metadata;
  963. break;
  964. }
  965. if(NULL != last && last->fileName().compare(metadata->file_name) == 0) {
  966. free_metadata(metadata);
  967. delete metadata;
  968. break;
  969. }
  970. items.push_back(metadata);
  971. }
  972. if(plugin->flags() & sf_reverse) {
  973. items.reverse();
  974. }
  975. int number = 0;
  976. if(plugin->flags() & sf_numbered) {
  977. if(NULL == last) {
  978. number = 1;
  979. }
  980. else {
  981. size_t pos = last->title().find(" ");
  982. if(pos != string::npos) {
  983. string tmp = last->title().substr(0, pos);
  984. number = atoi(tmp.c_str());
  985. }
  986. }
  987. }
  988. std::list<metadata_t*>::iterator iter;
  989. for(iter = items.begin(); iter != items.end(); iter++) {
  990. number++;
  991. }
  992. */
  993. //delete metadata;
  994. /*
  995. * DbObject obj;
  996. ObjectDetails details;
  997. VideoItem item;
  998. *
  999. while(plugin->next(item.metadata())) {
  1000. cout << "NEXT BLIP ITEM: " << item.title() <<endl;
  1001. obj.reset();
  1002. obj.setType(ITEM_VIDEO_ITEM_VIDEO_BROADCAST);
  1003. obj.setTitle(item.title());
  1004. obj.setPath(item.url());
  1005. obj.setParentId(nc.objectId());
  1006. details.reset();
  1007. details = item;
  1008. details.setAlbumArtExt("fail");
  1009. details.setUpdated();
  1010. details.save();
  1011. obj.setDetailId(details.id());
  1012. obj.save();
  1013. obj.setUpdated();
  1014. obj.save();
  1015. }
  1016. */
  1017. /* delete source;
  1018. delete base;
  1019. plugin->close();
  1020. delete plugin;*/
  1021. }
  1022. bool CContentDatabase::exportData(std::string fileName, std::string path, bool remove) // static
  1023. {
  1024. Database::ConnectionParameters params;
  1025. params.filename = fileName;
  1026. Plugins::DatabasePlugin* sqlitePlugin = NULL; //CPluginMgr::databasePlugin("sqlite3");
  1027. if(!sqlitePlugin)
  1028. return false;
  1029. Database::Connection* connection = NULL; //sqlitePlugin->createConnection();
  1030. if(!connection)
  1031. return false;
  1032. connection->setParameters(params);
  1033. if(!connection->connect()) {
  1034. delete connection;
  1035. return false;
  1036. }
  1037. stringstream sql;
  1038. // get from local db
  1039. //SQLQuery get;
  1040. // write to new connection
  1041. // SQLQuery set(connection);
  1042. /*sql << set.build(SQL_TABLES_EXIST, 0);
  1043. set.select(sql.str());
  1044. sql.str("");
  1045. if(!set.eof()) {
  1046. set.exec("drop table FUPPES_DB_INFO");
  1047. set.exec("drop table OBJECTS");
  1048. set.exec("drop table OBJECT_DETAILS");
  1049. }
  1050. // create tables
  1051. sql << set.build(SQL_CREATE_TABLE_DB_INFO, 0);
  1052. set.exec(sql.str());
  1053. sql.str("");
  1054. sql << set.build(SQL_CREATE_TABLE_OBJECTS, 0);
  1055. set.exec(sql.str());
  1056. sql.str("");
  1057. sql << set.build(SQL_CREATE_TABLE_OBJECT_DETAILS, 0);
  1058. set.exec(sql.str());
  1059. sql.str("");
  1060. sql << set.build(SQL_SET_DB_INFO, DB_VERSION);
  1061. set.exec(sql.str());
  1062. sql.str("");
  1063. */
  1064. // get all objects in path
  1065. path = Directory::appendTrailingSlash(path);
  1066. sql.str("");
  1067. sql << "select * from OBJECTS where " << "PATH like '" << SQLEscape(path) << "%' and " << "DEVICE is NULL";
  1068. /*DbObject* in;
  1069. DbObject* out;
  1070. ObjectDetails details;
  1071. object_id_t oid = 0;
  1072. get.select(sql.str());
  1073. cout << "START EXPORT" << endl;
  1074. while (!get.eof()) {
  1075. oid++;
  1076. in = new DbObject(get.result());
  1077. out = new DbObject(in);
  1078. cout << "export OBJECT: " << in->title() << "*" << endl;
  1079. // export details
  1080. if(in->detailId() > 0) {
  1081. details.reset();
  1082. details = *in->details();
  1083. details.save(&set);
  1084. out->setDetailId(details.id());
  1085. }
  1086. // export object
  1087. string newPath = in->path();
  1088. newPath = "./" + StringReplace(newPath, path, "");
  1089. out->setPath(newPath);
  1090. out->setObjectId(in->objectId());
  1091. out->save(&set);
  1092. delete out;
  1093. // delete object from local db
  1094. delete in;
  1095. get.next();
  1096. }
  1097. cout << "EXPORT FINISHED" << endl;
  1098. */
  1099. delete connection;
  1100. return true;
  1101. }
  1102. bool CContentDatabase::importData(std::string fileName, std::string path) // static
  1103. {
  1104. Database::ConnectionParameters params;
  1105. params.filename = fileName;
  1106. Plugins::DatabasePlugin* sqlitePlugin = NULL; //CPluginMgr::databasePlugin("sqlite3");
  1107. if(!sqlitePlugin)
  1108. return false;
  1109. Database::Connection* connection = NULL; //sqlitePlugin->createConnection();
  1110. if(!connection)
  1111. return false;
  1112. connection->setParameters(params);
  1113. if(!connection->connect()) {
  1114. delete connection;
  1115. return false;
  1116. }
  1117. stringstream sql;
  1118. // get from new connection
  1119. // SQLQuery get(connection);
  1120. // write to local db
  1121. // SQLQuery set;
  1122. delete connection;
  1123. return false;
  1124. }