|
MythTV
0.26-pre
|
00001 #include <memory> // for std::auto_ptr 00002 #include <algorithm> 00003 #include <iterator> 00004 #include <map> 00005 00006 #include <QFileInfo> 00007 #include <QList> 00008 00009 #include "mythcontext.h" 00010 #include "mythmiscutil.h" 00011 00012 #include "mythgenerictree.h" 00013 #include "videometadatalistmanager.h" 00014 #include "dbaccess.h" 00015 #include "quicksp.h" 00016 #include "dirscan.h" 00017 #include "videoutils.h" 00018 #include "parentalcontrols.h" 00019 00020 #include "videofilter.h" 00021 #include "videolist.h" 00022 #include "videodlg.h" 00023 00024 #include "upnpscanner.h" 00025 00026 class TreeNodeDataPrivate 00027 { 00028 public: 00029 TreeNodeDataPrivate(VideoMetadata *metadata) : 00030 m_metadata(metadata) 00031 { 00032 if (m_metadata) 00033 m_host = m_metadata->GetHost(); 00034 else 00035 m_host = ""; 00036 } 00037 00038 TreeNodeDataPrivate(QString path, QString host, QString prefix) : 00039 m_metadata(0), m_host(host), m_path(path), m_prefix(prefix) 00040 { 00041 } 00042 00043 VideoMetadata *GetMetadata() 00044 { 00045 return m_metadata; 00046 } 00047 00048 const VideoMetadata *GetMetadata() const 00049 { 00050 return m_metadata; 00051 } 00052 00053 QString GetPath() const 00054 { 00055 return m_path; 00056 } 00057 00058 QString GetHost() const 00059 { 00060 return m_host; 00061 } 00062 00063 QString GetPrefix() const 00064 { 00065 return m_prefix; 00066 } 00067 00068 private: 00069 VideoMetadata *m_metadata; 00070 QString m_host; 00071 QString m_path; 00072 QString m_prefix; 00073 }; 00074 00075 TreeNodeData::TreeNodeData() : m_d(0) 00076 { 00077 } 00078 00079 TreeNodeData::TreeNodeData(VideoMetadata *metadata) 00080 { 00081 m_d = new TreeNodeDataPrivate(metadata); 00082 } 00083 00084 TreeNodeData::TreeNodeData(QString path, QString host, QString prefix) 00085 { 00086 m_d = new TreeNodeDataPrivate(path, host, prefix); 00087 } 00088 00089 TreeNodeData::TreeNodeData(const TreeNodeData &other) : m_d(0) 00090 { 00091 *this = other; 00092 } 00093 00094 TreeNodeData &TreeNodeData::operator=(const TreeNodeData &rhs) 00095 { 00096 if (this != &rhs) 00097 { 00098 delete m_d; 00099 m_d = new TreeNodeDataPrivate(*rhs.m_d); 00100 } 00101 00102 return *this; 00103 } 00104 00105 TreeNodeData::~TreeNodeData() 00106 { 00107 delete m_d; 00108 } 00109 00110 VideoMetadata *TreeNodeData::GetMetadata() 00111 { 00112 if (m_d) 00113 return m_d->GetMetadata(); 00114 00115 return NULL; 00116 } 00117 00118 const VideoMetadata *TreeNodeData::GetMetadata() const 00119 { 00120 if (m_d) 00121 return m_d->GetMetadata(); 00122 00123 return NULL; 00124 } 00125 00126 QString TreeNodeData::GetPath() const 00127 { 00128 if (m_d) 00129 return m_d->GetPath(); 00130 return QString(); 00131 } 00132 00133 QString TreeNodeData::GetHost() const 00134 { 00135 if (m_d) 00136 return m_d->GetHost(); 00137 return QString(); 00138 } 00139 00140 QString TreeNodeData::GetPrefix() const 00141 { 00142 if (m_d) 00143 return m_d->GetPrefix(); 00144 return QString(); 00145 } 00146 00148 struct metadata_sort 00149 { 00150 metadata_sort(const VideoFilterSettings &vfs, bool sort_ignores_case) : 00151 m_vfs(vfs), m_sic(sort_ignores_case) {} 00152 00153 bool operator()(const VideoMetadata *lhs, const VideoMetadata *rhs) 00154 { 00155 return m_vfs.meta_less_than(*lhs, *rhs, m_sic); 00156 } 00157 00158 bool operator()(const smart_meta_node &lhs, const smart_meta_node &rhs) 00159 { 00160 return m_vfs.meta_less_than(*(lhs->getData()), *(rhs->getData()), 00161 m_sic); 00162 } 00163 00164 private: 00165 const VideoFilterSettings &m_vfs; 00166 bool m_sic; 00167 }; 00168 00169 struct metadata_path_sort 00170 { 00171 metadata_path_sort(bool ignore_case) : m_ignore_case(ignore_case) {} 00172 00173 bool operator()(const VideoMetadata &lhs, const VideoMetadata &rhs) 00174 { 00175 return sort(&lhs, &rhs); 00176 } 00177 00178 bool operator()(const VideoMetadata *lhs, const VideoMetadata *rhs) 00179 { 00180 return sort(lhs, rhs); 00181 } 00182 00183 bool operator()(const smart_dir_node &lhs, const smart_dir_node &rhs) 00184 { 00185 return sort(lhs->getPath(), rhs->getPath()); 00186 } 00187 00188 private: 00189 bool sort(const VideoMetadata *lhs, const VideoMetadata *rhs) 00190 { 00191 return sort(lhs->GetFilename(), rhs->GetFilename()); 00192 } 00193 00194 bool sort(const QString &lhs, const QString &rhs) 00195 { 00196 QString lhs_comp(lhs); 00197 QString rhs_comp(rhs); 00198 if (m_ignore_case) 00199 { 00200 lhs_comp = lhs_comp.toLower(); 00201 rhs_comp = rhs_comp.toLower(); 00202 } 00203 return QString::localeAwareCompare(lhs_comp, rhs_comp) < 0; 00204 } 00205 00206 bool m_ignore_case; 00207 }; 00208 00209 static QString path_to_node_name(const QString &path) 00210 { 00211 QString ret; 00212 int slashLoc = path.lastIndexOf('/', -2) + 1; 00213 if (path.right(1) == "/") 00214 ret = path.mid(slashLoc, path.length() - slashLoc - 1); 00215 else 00216 ret = path.mid(slashLoc); 00217 00218 return ret; 00219 } 00220 00221 static meta_dir_node *AddMetadataToDir(VideoMetadata *metadata, 00222 meta_dir_node *dir, 00223 meta_dir_node *hint = NULL) 00224 { 00225 meta_dir_node *start = dir; 00226 QString insert_chunk = metadata->GetFilename(); 00227 QString host = metadata->GetHost(); 00228 QString prefix = metadata->GetPrefix(); 00229 00230 if (hint) 00231 { 00232 if (metadata->GetFilename().startsWith(hint->getFQPath() + "/")) 00233 { 00234 start = hint; 00235 insert_chunk = 00236 metadata->GetFilename().mid(hint->getFQPath().length()); 00237 } 00238 } 00239 00240 if (insert_chunk.startsWith(dir->getFQPath() + "/")) 00241 { 00242 insert_chunk = metadata->GetFilename().mid(dir->getFQPath().length()); 00243 } 00244 00245 QStringList path = insert_chunk.split("/", QString::SkipEmptyParts); 00246 if (path.size() > 1) 00247 { 00248 path.pop_back(); 00249 } 00250 else 00251 { 00252 path.clear(); 00253 } 00254 00255 for (QStringList::const_iterator p = path.begin(); p != path.end(); ++p) 00256 { 00257 smart_dir_node sdn = start->addSubDir(*p, "" , host, prefix); 00258 start = sdn.get(); 00259 } 00260 00261 start->addEntry(smart_meta_node(new meta_data_node(metadata))); 00262 00263 return start; 00264 } 00265 00266 struct to_metadata_ptr 00267 { 00268 VideoMetadata *operator()(smart_meta_node &smn) 00269 { 00270 return smn->getData(); 00271 } 00272 00273 VideoMetadata *operator()(VideoMetadata &data) 00274 { 00275 return &data; 00276 } 00277 00278 VideoMetadata *operator()(const VideoMetadataListManager::VideoMetadataPtr &data) 00279 { 00280 return data.get(); 00281 } 00282 }; 00283 00284 // TODO: AEW We don't actually use this now 00285 // Ordering of items on a tree level (low -> high) 00286 enum NodeOrder { 00287 kOrderUp, 00288 kOrderSub, 00289 kOrderItem 00290 }; 00291 00292 static MythGenericTree *AddDirNode(MythGenericTree *where_to_add, 00293 QString name, QString fqPath, bool add_up_dirs, 00294 QString host = "", QString prefix = "") 00295 { 00296 // Add the subdir node... 00297 MythGenericTree *sub_node = 00298 where_to_add->addNode(name, kSubFolder, false); 00299 sub_node->setAttribute(kNodeSort, kOrderSub); 00300 sub_node->setOrderingIndex(kNodeSort); 00301 sub_node->SetData(QVariant::fromValue(TreeNodeData(fqPath, host, prefix))); 00302 sub_node->SetText(name, "title"); 00303 sub_node->DisplayState("subfolder", "nodetype"); 00304 00305 // ...and the updir node. 00306 if (add_up_dirs) 00307 { 00308 MythGenericTree *up_node = 00309 sub_node->addNode(where_to_add->getString(), kUpFolder, 00310 true, false); 00311 up_node->setAttribute(kNodeSort, kOrderUp); 00312 up_node->setOrderingIndex(kNodeSort); 00313 up_node->DisplayState("subfolder", "nodetype"); 00314 } 00315 00316 return sub_node; 00317 } 00318 00319 static int AddFileNode(MythGenericTree *where_to_add, QString name, 00320 VideoMetadata *metadata) 00321 { 00322 MythGenericTree *sub_node = where_to_add->addNode(name, 0, true); 00323 sub_node->setAttribute(kNodeSort, kOrderItem); 00324 sub_node->setOrderingIndex(kNodeSort); 00325 sub_node->SetData(QVariant::fromValue(TreeNodeData(metadata))); 00326 00327 // Text 00328 QHash<QString, QString> textMap; 00329 metadata->toMap(textMap); 00330 sub_node->SetTextFromMap(textMap); 00331 00332 // Images 00333 QHash<QString, QString> imageMap; 00334 metadata->GetImageMap(imageMap); 00335 sub_node->SetImageFromMap(imageMap); 00336 sub_node->SetImage("buttonimage", imageMap["smartimage"]); 00337 00338 // Assign images to parent node if this is the first child 00339 if (where_to_add->visibleChildCount() == 1 && 00340 where_to_add->getInt() == kSubFolder) 00341 { 00342 where_to_add->SetImageFromMap(imageMap); 00343 where_to_add->SetImage("buttonimage", imageMap["smartimage"]); 00344 } 00345 00346 // Statetypes 00347 QHash<QString, QString> stateMap; 00348 metadata->GetStateMap(stateMap); 00349 sub_node->DisplayStateFromMap(stateMap); 00350 00351 return 1; 00352 } 00353 00354 class VideoListImp 00355 { 00356 public: 00357 typedef std::vector<VideoMetadata *> metadata_view_list; 00358 00359 private: 00360 enum metadata_list_type { ltNone, ltFileSystem, ltDBMetadata, 00361 ltDBGenreGroup, ltDBCategoryGroup, 00362 ltDBYearGroup, ltDBDirectorGroup, 00363 ltDBStudioGroup, ltDBCastGroup, 00364 ltDBUserRatingGroup, ltDBInsertDateGroup, 00365 ltTVMetadata}; 00366 typedef VideoMetadataListManager::metadata_list metadata_list; 00367 typedef VideoMetadataListManager::VideoMetadataPtr MetadataPtr; 00368 00369 public: 00370 VideoListImp(); 00371 00372 void build_generic_tree(MythGenericTree *dst, meta_dir_node *src, 00373 bool include_updirs); 00374 MythGenericTree *buildVideoList(bool filebrowser, bool flatlist, 00375 int group_type, const ParentalLevel &parental_level, 00376 bool include_updirs); 00377 00378 void refreshList(bool filebrowser, const ParentalLevel &parental_level, 00379 bool flatlist, int group_type); 00380 bool refreshNode(MythGenericTree *node); 00381 00382 unsigned int count() const 00383 { 00384 return m_metadata_view_flat.size(); 00385 } 00386 00387 const VideoFilterSettings &getCurrentVideoFilter() const 00388 { 00389 return m_video_filter; 00390 } 00391 00392 void setCurrentVideoFilter(const VideoFilterSettings &filter) 00393 { 00394 m_video_filter = filter; 00395 } 00396 00397 int TryFilter(const VideoFilterSettings &filter) const 00398 { 00399 int ret = 0; 00400 for (metadata_list::const_iterator p = m_metadata.getList().begin(); 00401 p != m_metadata.getList().end(); ++p) 00402 { 00403 if (filter.matches_filter(**p)) ++ret; 00404 } 00405 return ret; 00406 } 00407 00408 const VideoMetadataListManager &getListCache() const 00409 { 00410 return m_metadata; 00411 } 00412 00413 unsigned int getFilterChangedState() 00414 { 00415 return m_video_filter.getChangedState(); 00416 } 00417 00418 bool Delete(unsigned int video_id, VideoList &dummy) 00419 { 00420 bool ret = false; 00421 MetadataPtr mp = m_metadata.byID(video_id); 00422 if (mp) 00423 { 00424 ret = mp->DeleteFile(); 00425 if (ret) ret = m_metadata.purgeByID(video_id); 00426 } 00427 00428 return ret; 00429 } 00430 00431 MythGenericTree *GetTreeRoot() 00432 { 00433 return video_tree_root.get(); 00434 } 00435 00436 void InvalidateCache() { 00437 // Set the type to none to avoid refreshList thinking it doesn't 00438 // need to. 00439 m_metadata_list_type = VideoListImp::ltNone; 00440 00441 metadata_list ml; 00442 VideoMetadataListManager::loadAllFromDatabase(ml); 00443 m_metadata.setList(ml); 00444 } 00445 00446 private: 00447 void sort_view_data(bool flat_list); 00448 void fillMetadata(metadata_list_type whence); 00449 00450 void buildFsysList(); 00451 void buildGroupList(metadata_list_type whence); 00452 void buildDbList(); 00453 void buildTVList(); 00454 void buildFileList(smart_dir_node &directory, metadata_list &metalist, 00455 const QString &prefix); 00456 00457 void update_meta_view(bool flat_list); 00458 00459 private: 00460 bool m_ListUnknown; 00461 bool m_LoadMetaData; 00462 00463 std::auto_ptr<MythGenericTree> video_tree_root; 00464 00465 VideoMetadataListManager m_metadata; 00466 meta_dir_node m_metadata_tree; // master list for tree views 00467 00468 metadata_view_list m_metadata_view_flat; 00469 meta_dir_node m_metadata_view_tree; 00470 00471 metadata_list_type m_metadata_list_type; 00472 00473 VideoFilterSettings m_video_filter; 00474 }; 00475 00476 VideoList::VideoList() 00477 { 00478 m_imp = new VideoListImp; 00479 } 00480 00481 VideoList::~VideoList() 00482 { 00483 delete m_imp; 00484 } 00485 00486 MythGenericTree *VideoList::buildVideoList(bool filebrowser, bool flatlist, 00487 int group_type, const ParentalLevel &parental_level, 00488 bool include_updirs) 00489 { 00490 return m_imp->buildVideoList(filebrowser, flatlist, 00491 group_type, parental_level, include_updirs); 00492 } 00493 00494 void VideoList::refreshList(bool filebrowser, 00495 const ParentalLevel &parental_level, 00496 bool flat_list, int group_type) 00497 { 00498 m_imp->refreshList(filebrowser, parental_level, flat_list, group_type); 00499 } 00500 00501 bool VideoList::refreshNode(MythGenericTree *node) 00502 { 00503 return m_imp->refreshNode(node); 00504 } 00505 00506 unsigned int VideoList::count() const 00507 { 00508 return m_imp->count(); 00509 } 00510 00511 const VideoFilterSettings &VideoList::getCurrentVideoFilter() const 00512 { 00513 return m_imp->getCurrentVideoFilter(); 00514 } 00515 00516 void VideoList::setCurrentVideoFilter(const VideoFilterSettings &filter) 00517 { 00518 m_imp->setCurrentVideoFilter(filter); 00519 } 00520 00521 int VideoList::TryFilter(const VideoFilterSettings &filter) const 00522 { 00523 return m_imp->TryFilter(filter); 00524 } 00525 00526 const VideoMetadataListManager &VideoList::getListCache() const 00527 { 00528 return m_imp->getListCache(); 00529 } 00530 00531 unsigned int VideoList::getFilterChangedState() 00532 { 00533 return m_imp->getFilterChangedState(); 00534 } 00535 00536 bool VideoList::Delete(int video_id) 00537 { 00538 return m_imp->Delete(video_id, *this); 00539 } 00540 00541 MythGenericTree *VideoList::GetTreeRoot() 00542 { 00543 return m_imp->GetTreeRoot(); 00544 } 00545 00546 void VideoList::InvalidateCache() 00547 { 00548 return m_imp->InvalidateCache(); 00549 } 00550 00552 // VideoListImp 00554 VideoListImp::VideoListImp() : m_metadata_view_tree("", "top"), 00555 m_metadata_list_type(ltNone) 00556 { 00557 m_ListUnknown = gCoreContext->GetNumSetting("VideoListUnknownFileTypes", 0); 00558 00559 m_LoadMetaData = gCoreContext->GetNumSetting("VideoTreeLoadMetaData", 0); 00560 } 00561 00562 void VideoListImp::build_generic_tree(MythGenericTree *dst, meta_dir_node *src, 00563 bool include_updirs) 00564 { 00565 if (src->DataIsValid()) 00566 { 00567 dst->setInt(kDynamicSubFolder); 00568 dst->SetData(src->GetData()); 00569 return; 00570 } 00571 00572 for (meta_dir_node::const_dir_iterator dir = src->dirs_begin(); 00573 dir != src->dirs_end(); ++dir) 00574 { 00575 if ((*dir)->has_entries()) 00576 { 00577 bool incUpDir = include_updirs; 00578 // Only include upnodes when there is a parent to move up to 00579 if (!dst->getParent()) 00580 incUpDir = false; 00581 00582 MythGenericTree *t = AddDirNode(dst, (*dir)->getName(), 00583 (*dir)->getFQPath(), incUpDir, (*dir)->GetHost(), 00584 (*dir)->GetPrefix()); 00585 00586 build_generic_tree(t, dir->get(), include_updirs); 00587 } 00588 } 00589 00590 for (meta_dir_node::const_entry_iterator entry = src->entries_begin(); 00591 entry != src->entries_end(); ++entry) 00592 { 00593 if (((*entry)->getData()->GetSeason() > 0) || 00594 ((*entry)->getData()->GetEpisode() > 0)) 00595 { 00596 QString seas = QString::number((*entry)->getData()->GetSeason()); 00597 QString ep = QString::number((*entry)->getData()->GetEpisode()); 00598 QString title = (*entry)->getData()->GetTitle(); 00599 QString subtitle = (*entry)->getData()->GetSubtitle(); 00600 if (ep.size() < 2) 00601 ep.prepend("0"); 00602 QString displayTitle = QString("%1 %2x%3 - %4").arg(title).arg(seas) 00603 .arg(ep) 00604 .arg(subtitle); 00605 if (src->getName() == title) 00606 displayTitle = QString("%2x%3 - %4").arg(seas).arg(ep) 00607 .arg(subtitle); 00608 00609 AddFileNode(dst, displayTitle, (*entry)->getData()); 00610 } 00611 else if ((*entry)->getData()->GetSubtitle().isEmpty()) 00612 AddFileNode(dst, (*entry)->getData()->GetTitle(), (*entry)->getData()); 00613 else 00614 { 00615 QString TitleSub = QString("%1 - %2").arg((*entry)->getData()->GetTitle()) 00616 .arg((*entry)->getData()->GetSubtitle()); 00617 AddFileNode(dst, TitleSub, (*entry)->getData()); 00618 } 00619 } 00620 } 00621 00622 // Build a generic tree containing the video files. You can control the 00623 // contents and the shape of the tree in de following ways: 00624 // filebrowser: 00625 // If true, the actual state of the filesystem is examined. If a video 00626 // is already known to the system, this info is retrieved. If not, some 00627 // basic info is provided. 00628 // If false, only video information already present in the database is 00629 // presented. 00630 // flatlist: 00631 // If true, the tree is reduced to a single level containing all the 00632 // videos found. 00633 // If false, the hierarchy present on the filesystem or in the database 00634 // is preserved. In this mode, both sub-dirs and updirs are present. 00635 MythGenericTree *VideoListImp::buildVideoList(bool filebrowser, bool flatlist, 00636 int group_type, 00637 const ParentalLevel &parental_level, 00638 bool include_updirs) 00639 { 00640 refreshList(filebrowser, parental_level, flatlist, group_type); 00641 00642 typedef std::map<QString, MythGenericTree *> string_to_tree; 00643 string_to_tree prefix_tree_map; 00644 00645 video_tree_root.reset(new MythGenericTree(QObject::tr("Video Home"), 00646 kRootNode, false)); 00647 00648 build_generic_tree(video_tree_root.get(), &m_metadata_view_tree, 00649 include_updirs); 00650 00651 if (m_metadata_view_flat.empty()) 00652 { 00653 video_tree_root.reset(new MythGenericTree(QObject::tr("Video Home"), 00654 kRootNode, false)); 00655 video_tree_root.get()->addNode(QObject::tr("No files found"), 00656 kNoFilesFound, false); 00657 } 00658 00659 return video_tree_root.get(); 00660 } 00661 00662 bool VideoListImp::refreshNode(MythGenericTree *node) 00663 { 00664 if (!node) 00665 return false; 00666 00667 // node->GetData() provides information on how/where to refresh the 00668 // data for this node 00669 00670 QVariant data = node->GetData(); 00671 if (!data.isValid()) 00672 return false; 00673 00674 // currently only UPNPScanner can refresh data 00675 if (UPNPScanner::Instance() && UPNPScanner::Instance()->GetMetadata(data)) 00676 { 00677 // force a refresh 00678 m_metadata_list_type = VideoListImp::ltNone; 00679 return true; 00680 } 00681 00682 return false; 00683 } 00684 00685 void VideoListImp::refreshList(bool filebrowser, 00686 const ParentalLevel &parental_level, 00687 bool flat_list, int group_type) 00688 { 00689 00690 m_video_filter.setParentalLevel(parental_level.GetLevel()); 00691 00692 if (filebrowser) 00693 { 00694 fillMetadata(ltFileSystem); 00695 } 00696 else 00697 { 00698 switch (group_type) 00699 { 00700 case VideoDialog::BRS_FOLDER: 00701 fillMetadata(ltDBMetadata); 00702 LOG(VB_GENERAL, LOG_DEBUG, "Using Folder mode"); 00703 break; 00704 case VideoDialog::BRS_GENRE: 00705 fillMetadata(ltDBGenreGroup); 00706 LOG(VB_GENERAL, LOG_DEBUG, "Using Genre mode"); 00707 break; 00708 case VideoDialog::BRS_CATEGORY: 00709 fillMetadata(ltDBCategoryGroup); 00710 LOG(VB_GENERAL, LOG_DEBUG, "Using Category mode"); 00711 break; 00712 case VideoDialog::BRS_YEAR: 00713 fillMetadata(ltDBYearGroup); 00714 LOG(VB_GENERAL, LOG_DEBUG, "Using Year mode"); 00715 break; 00716 case VideoDialog::BRS_DIRECTOR: 00717 fillMetadata(ltDBDirectorGroup); 00718 LOG(VB_GENERAL, LOG_DEBUG, "Using Director mode"); 00719 break; 00720 case VideoDialog::BRS_STUDIO: 00721 fillMetadata(ltDBStudioGroup); 00722 LOG(VB_GENERAL, LOG_DEBUG, "Using Studio mode"); 00723 break; 00724 case VideoDialog::BRS_CAST: 00725 fillMetadata(ltDBCastGroup); 00726 LOG(VB_GENERAL, LOG_DEBUG, "Using Cast Mode"); 00727 break; 00728 case VideoDialog::BRS_USERRATING: 00729 fillMetadata(ltDBUserRatingGroup); 00730 LOG(VB_GENERAL, LOG_DEBUG, "Using User Rating Mode"); 00731 break; 00732 case VideoDialog::BRS_INSERTDATE: 00733 fillMetadata(ltDBInsertDateGroup); 00734 LOG(VB_GENERAL, LOG_DEBUG, "Using Insert Date Mode"); 00735 break; 00736 case VideoDialog::BRS_TVMOVIE: 00737 fillMetadata(ltTVMetadata); 00738 LOG(VB_GENERAL, LOG_DEBUG, "Using TV/Movie Mode"); 00739 break; 00740 default: 00741 fillMetadata(ltDBMetadata); 00742 break; 00743 } 00744 } 00745 update_meta_view(flat_list); 00746 } 00747 00748 void VideoListImp::sort_view_data(bool flat_list) 00749 { 00750 if (flat_list) 00751 { 00752 std::sort(m_metadata_view_flat.begin(), m_metadata_view_flat.end(), 00753 metadata_sort(m_video_filter, true)); 00754 } 00755 else 00756 { 00757 m_metadata_view_tree.sort(metadata_path_sort(true), 00758 metadata_sort(m_video_filter, 00759 true)); 00760 } 00761 } 00762 00763 void VideoListImp::fillMetadata(metadata_list_type whence) 00764 { 00765 if (m_metadata_list_type != whence) 00766 { 00767 m_metadata_list_type = whence; 00768 // flush existing data 00769 metadata_list ml; 00770 m_metadata.setList(ml); 00771 m_metadata_tree.clear(); 00772 00773 switch (whence) 00774 { 00775 case ltFileSystem: 00776 buildFsysList(); 00777 break; 00778 case ltDBMetadata: 00779 buildDbList(); 00780 break; 00781 case ltTVMetadata: 00782 buildTVList(); 00783 break; 00784 case ltDBGenreGroup: 00785 case ltDBCategoryGroup: 00786 case ltDBYearGroup: 00787 case ltDBDirectorGroup: 00788 case ltDBStudioGroup: 00789 case ltDBCastGroup: 00790 case ltDBUserRatingGroup: 00791 case ltDBInsertDateGroup: 00792 buildGroupList(whence); 00793 break; 00794 case ltNone: 00795 break; 00796 } 00797 } 00798 } 00799 00800 void VideoListImp::buildGroupList(metadata_list_type whence) 00801 { 00802 metadata_list ml; 00803 VideoMetadataListManager::loadAllFromDatabase(ml); 00804 m_metadata.setList(ml); 00805 00806 metadata_view_list mlist; 00807 mlist.reserve(m_metadata.getList().size()); 00808 00809 std::back_insert_iterator<metadata_view_list> mli(mlist); 00810 std::transform(m_metadata.getList().begin(), m_metadata.getList().end(), 00811 mli, to_metadata_ptr()); 00812 00813 metadata_path_sort mps(true); 00814 std::sort(mlist.begin(), mlist.end(), mps); 00815 00816 typedef std::map<QString, meta_dir_node *> group_to_node_map; 00817 group_to_node_map gtnm; 00818 00819 meta_dir_node *video_root = &m_metadata_tree; 00820 00821 smart_dir_node sdn = video_root->addSubDir("All"); 00822 meta_dir_node* all_group_node = sdn.get(); 00823 00824 for (metadata_view_list::iterator p = mlist.begin(); p != mlist.end(); ++p) 00825 { 00826 VideoMetadata *data = *p; 00827 00828 all_group_node->addEntry(smart_meta_node(new meta_data_node(data))); 00829 00830 std::vector <QString> groups; 00831 00832 switch (whence) 00833 { 00834 case ltDBGenreGroup: 00835 { 00836 std::vector <std::pair <int, QString> > genres = 00837 data->GetGenres(); 00838 00839 for (std::vector <std::pair <int, QString> >::iterator i = 00840 genres.begin(); i != genres.end(); ++i) 00841 { 00842 std::pair <int, QString> item = *i; 00843 groups.push_back(item.second); 00844 } 00845 break; 00846 } 00847 case ltDBCategoryGroup: 00848 { 00849 groups.push_back(data->GetCategory()); 00850 break; 00851 } 00852 case ltDBYearGroup: 00853 { 00854 groups.push_back(QString::number(data->GetYear())); 00855 break; 00856 } 00857 case ltDBDirectorGroup: 00858 { 00859 groups.push_back(data->GetDirector()); 00860 break; 00861 } 00862 case ltDBStudioGroup: 00863 { 00864 groups.push_back(data->GetStudio()); 00865 break; 00866 } 00867 case ltDBCastGroup: 00868 { 00869 std::vector <std::pair <int, QString> > cast = data->GetCast(); 00870 00871 for (std::vector <std::pair <int, QString> >::iterator i = 00872 cast.begin(); i != cast.end(); ++i) 00873 { 00874 std::pair <int, QString> item = *i; 00875 groups.push_back(item.second); 00876 } 00877 break; 00878 } 00879 case ltDBUserRatingGroup: 00880 { 00881 int i = data->GetUserRating(); 00882 groups.push_back(QString::number(i)); 00883 break; 00884 } 00885 case ltDBInsertDateGroup: 00886 { 00887 QDate date = data->GetInsertdate(); 00888 groups.push_back(MythDateToString(date, kDateFull | kSimplify)); 00889 break; 00890 } 00891 default: 00892 { 00893 LOG(VB_GENERAL, LOG_ERR, "Invalid type of grouping"); 00894 break; 00895 } 00896 } 00897 00898 if (groups.empty()) 00899 { 00900 meta_dir_node *group_node = gtnm["Unknown"]; 00901 00902 if (group_node == NULL) 00903 { 00904 smart_dir_node sdn = video_root->addSubDir("Unknown"); 00905 group_node = sdn.get(); 00906 gtnm["Unknown"] = group_node; 00907 } 00908 00909 group_node->addEntry(smart_meta_node(new meta_data_node(data))); 00910 } 00911 00912 for (std::vector <QString>::iterator i = groups.begin(); 00913 i != groups.end(); ++i) 00914 { 00915 QString item = *i; 00916 00917 meta_dir_node *group_node = gtnm[item]; 00918 00919 if (group_node == NULL) 00920 { 00921 smart_dir_node sdn = video_root->addSubDir(item); 00922 group_node = sdn.get(); 00923 gtnm[item] = group_node; 00924 } 00925 00926 group_node->addEntry(smart_meta_node(new meta_data_node(data))); 00927 } 00928 } 00929 } 00930 00931 void VideoListImp::buildTVList() 00932 { 00933 metadata_list ml; 00934 VideoMetadataListManager::loadAllFromDatabase(ml); 00935 m_metadata.setList(ml); 00936 00937 metadata_view_list mlist; 00938 mlist.reserve(m_metadata.getList().size()); 00939 00940 std::back_insert_iterator<metadata_view_list> mli(mlist); 00941 std::transform(m_metadata.getList().begin(), m_metadata.getList().end(), 00942 mli, to_metadata_ptr()); 00943 00944 metadata_path_sort mps(true); 00945 std::sort(mlist.begin(), mlist.end(), mps); 00946 00947 typedef std::map<QString, meta_dir_node *> group_to_node_map; 00948 group_to_node_map gtnm; 00949 00950 meta_dir_node *video_root = &m_metadata_tree; 00951 00952 smart_dir_node sdn = video_root->addSubDir(QObject::tr("Television")); 00953 meta_dir_node* television_node = sdn.get(); 00954 00955 smart_dir_node vdn = video_root->addSubDir(QObject::tr("Movies")); 00956 meta_dir_node* movie_node = vdn.get(); 00957 00958 for (metadata_view_list::iterator p = mlist.begin(); p != mlist.end(); ++p) 00959 { 00960 VideoMetadata *data = *p; 00961 00962 if (((*p)->GetSeason() > 0) || ((*p)->GetEpisode() > 0)) 00963 { 00964 smart_dir_node sdn = television_node->addSubDir((*p)->GetTitle()); 00965 meta_dir_node* title_node = sdn.get(); 00966 00967 smart_dir_node ssdn = title_node->addSubDir(QObject::tr("Season %1") 00968 .arg((*p)->GetSeason())); 00969 meta_dir_node* season_node = ssdn.get(); 00970 00971 season_node->addEntry(smart_meta_node(new meta_data_node(data))); 00972 } 00973 else 00974 movie_node->addEntry(smart_meta_node(new meta_data_node(data))); 00975 } 00976 } 00977 00978 void VideoListImp::buildDbList() 00979 { 00980 metadata_list ml; 00981 VideoMetadataListManager::loadAllFromDatabase(ml); 00982 m_metadata.setList(ml); 00983 00984 metadata_view_list mlist; 00985 mlist.reserve(m_metadata.getList().size()); 00986 00987 std::back_insert_iterator<metadata_view_list> mli(mlist); 00988 std::transform(m_metadata.getList().begin(), m_metadata.getList().end(), 00989 mli, to_metadata_ptr()); 00990 00991 // print_meta_list(mlist); 00992 00993 metadata_path_sort mps(true); 00994 std::sort(mlist.begin(), mlist.end(), mps); 00995 00996 // TODO: break out the prefix in the DB so this isn't needed 00997 typedef std::map<QString, meta_dir_node *> prefix_to_node_map; 00998 prefix_to_node_map ptnm; 00999 01000 QStringList dirs = GetVideoDirs(); 01001 01002 if (!dirs.size()) 01003 return; 01004 01005 QString test_prefix(dirs[0]); 01006 01007 meta_dir_node *video_root = &m_metadata_tree; 01008 if (dirs.size() == 1) 01009 { 01010 video_root->setPathRoot(); 01011 video_root->setPath(test_prefix); 01012 video_root->setName("videos"); 01013 ptnm.insert(prefix_to_node_map::value_type(test_prefix, video_root)); 01014 } 01015 01016 for (metadata_view_list::iterator p = mlist.begin(); p != mlist.end(); ++p) 01017 { 01018 AddMetadataToDir(*p, video_root); 01019 } 01020 01021 // print_dir_tree(m_metadata_tree); // AEW DEBUG 01022 } 01023 01024 void VideoListImp::buildFsysList() 01025 { 01026 // 01027 // Fill metadata from directory structure 01028 // 01029 01030 typedef std::vector<std::pair<QString, QString> > node_to_path_list; 01031 01032 node_to_path_list node_paths; 01033 01034 QStringList dirs = GetVideoDirs(); 01035 if (dirs.size() > 1) 01036 { 01037 for (QStringList::const_iterator iter = dirs.begin(); 01038 iter != dirs.end(); ++iter) 01039 { 01040 node_paths.push_back( 01041 node_to_path_list::value_type(path_to_node_name(*iter), 01042 *iter)); 01043 } 01044 } 01045 else 01046 { 01047 node_paths.push_back( 01048 node_to_path_list::value_type(QObject::tr("videos"), dirs[0])); 01049 } 01050 01051 // 01052 // Add all root-nodes to the tree. 01053 // 01054 metadata_list ml; 01055 for (node_to_path_list::iterator p = node_paths.begin(); 01056 p != node_paths.end(); ++p) 01057 { 01058 smart_dir_node root = m_metadata_tree.addSubDir(p->second, p->first); 01059 root->setPathRoot(); 01060 01061 buildFileList(root, ml, p->second); 01062 } 01063 01064 // retrieve any MediaServer data that may be available 01065 if (UPNPScanner::Instance()) 01066 UPNPScanner::Instance()->GetInitialMetadata(&ml, &m_metadata_tree); 01067 01068 // See if we can find this filename in DB 01069 if (m_LoadMetaData) 01070 { 01071 // Load the DB data so metadata lookups work 01072 // TODO: ugly, pass the list 01073 VideoMetadataListManager mdlm; 01074 metadata_list db_metadata; 01075 VideoMetadataListManager::loadAllFromDatabase(db_metadata); 01076 mdlm.setList(db_metadata); 01077 for (metadata_list::iterator p = ml.begin(); p != ml.end(); ++p) 01078 { 01079 (*p)->FillDataFromFilename(mdlm); 01080 } 01081 } 01082 m_metadata.setList(ml); 01083 } 01084 01085 01086 static void copy_entries(meta_dir_node &dst, meta_dir_node &src, 01087 const VideoFilterSettings &filter) 01088 { 01089 for (meta_dir_node::entry_iterator e = src.entries_begin(); 01090 e != src.entries_end(); ++e) 01091 { 01092 if (filter.matches_filter(*((*e)->getData()))) 01093 { 01094 dst.addEntry( 01095 smart_meta_node(new meta_data_node((*e)->getData()))); 01096 } 01097 } 01098 } 01099 01100 static void copy_filtered_tree(meta_dir_node &dst, meta_dir_node &src, 01101 const VideoFilterSettings &filter) 01102 { 01103 copy_entries(dst, src, filter); 01104 for (meta_dir_node::dir_iterator dir = src.dirs_begin(); 01105 dir != src.dirs_end(); ++dir) 01106 { 01107 smart_dir_node sdn = dst.addSubDir((*dir)->getPath(), 01108 (*dir)->getName(), 01109 (*dir)->GetHost(), 01110 (*dir)->GetPrefix(), 01111 (*dir)->GetData()); 01112 copy_filtered_tree(*sdn, *(dir->get()), filter); 01113 } 01114 } 01115 01116 void tree_view_to_flat(meta_dir_node &tree, 01117 VideoListImp::metadata_view_list &flat); 01118 struct call_tree_flat 01119 { 01120 call_tree_flat(VideoListImp::metadata_view_list &list) : m_list(list) {} 01121 01122 void operator()(smart_dir_node &sdn) 01123 { 01124 tree_view_to_flat(*(sdn.get()), m_list); 01125 } 01126 01127 VideoListImp::metadata_view_list &m_list; 01128 }; 01129 01130 // Fills a flat view with pointers to all entries in a tree. 01131 void tree_view_to_flat(meta_dir_node &tree, 01132 VideoListImp::metadata_view_list &flat) 01133 { 01134 std::back_insert_iterator<VideoListImp::metadata_view_list> bip(flat); 01135 std::transform(tree.entries_begin(), tree.entries_end(), bip, 01136 to_metadata_ptr()); 01137 01138 std::for_each(tree.dirs_begin(), tree.dirs_end(), call_tree_flat(flat)); 01139 } 01140 01141 void VideoListImp::update_meta_view(bool flat_list) 01142 { 01143 m_metadata_view_flat.clear(); 01144 m_metadata_view_flat.reserve(m_metadata.getList().size()); 01145 01146 m_metadata_view_tree.clear(); 01147 01148 // a big punt on setting the sort key 01149 // TODO: put this in the DB, half the time in this function is spent 01150 // doing this. 01151 for (metadata_list::const_iterator si = m_metadata.getList().begin(); 01152 si != m_metadata.getList().end(); ++si) 01153 { 01154 if (!(*si)->HasSortKey()) 01155 { 01156 VideoMetadata::SortKey skey = 01157 VideoMetadata::GenerateDefaultSortKey(*(*si), true); 01158 (*si)->SetSortKey(skey); 01159 } 01160 } 01161 01162 if (flat_list) 01163 { 01164 for (metadata_list::const_iterator p = m_metadata.getList().begin(); 01165 p != m_metadata.getList().end(); ++p) 01166 { 01167 if (m_video_filter.matches_filter(*(*p))) 01168 { 01169 m_metadata_view_flat.push_back(p->get()); 01170 } 01171 } 01172 01173 sort_view_data(flat_list); 01174 01175 for (metadata_view_list::iterator p = m_metadata_view_flat.begin(); 01176 p != m_metadata_view_flat.end(); ++p) 01177 { 01178 m_metadata_view_tree.addEntry(new meta_data_node(*p)); 01179 } 01180 } 01181 else 01182 { 01183 m_metadata_view_tree.setPath(m_metadata_tree.getPath()); 01184 m_metadata_view_tree.setName(m_metadata_tree.getName()); 01185 copy_filtered_tree(m_metadata_view_tree, m_metadata_tree, 01186 m_video_filter); 01187 01188 sort_view_data(flat_list); 01189 01190 tree_view_to_flat(m_metadata_view_tree, m_metadata_view_flat); 01191 } 01192 } 01193 01194 class dirhandler : public DirectoryHandler 01195 { 01196 public: 01197 typedef std::list<simple_ref_ptr<DirectoryHandler> > free_list; 01198 01199 public: 01200 dirhandler(smart_dir_node &directory, const QString &prefix, 01201 VideoMetadataListManager::metadata_list &metalist, 01202 free_list &dh_free_list, bool infer_title) : 01203 m_directory(directory), m_prefix(prefix), m_metalist(metalist), 01204 m_dh_free_list(dh_free_list), m_infer_title(infer_title) 01205 { 01206 } 01207 01208 DirectoryHandler *newDir(const QString &dir_name, 01209 const QString &fq_dir_name) 01210 { 01211 (void) fq_dir_name; 01212 smart_dir_node dir = m_directory->addSubDir(dir_name); 01213 DirectoryHandler *dh = new dirhandler(dir, m_prefix, m_metalist, 01214 m_dh_free_list, 01215 m_infer_title); 01216 m_dh_free_list.push_back(dh); 01217 return dh; 01218 } 01219 01220 void handleFile(const QString &file_name, 01221 const QString &fq_file_name, 01222 const QString &extension) 01223 { 01224 handleFile(file_name, fq_file_name, extension, ""); 01225 } 01226 01227 void handleFile(const QString &file_name, 01228 const QString &fq_file_name, 01229 const QString &extension, 01230 const QString &host) 01231 { 01232 (void) file_name; 01233 (void) extension; 01234 QString file_string(fq_file_name); 01235 01236 VideoMetadataListManager::VideoMetadataPtr myData(new VideoMetadata(file_string)); 01237 QFileInfo qfi(file_string); 01238 QString title = qfi.completeBaseName(); 01239 if (m_infer_title) 01240 { 01241 QString tmptitle(VideoMetadata::FilenameToMeta(file_string, 1)); 01242 if (tmptitle.length()) 01243 title = tmptitle; 01244 } 01245 myData->SetTitle(title); 01246 myData->SetPrefix(m_prefix); 01247 01248 myData->SetHost(host); 01249 m_metalist.push_back(myData); 01250 01251 m_directory->addEntry(new meta_data_node(myData.get())); 01252 } 01253 01254 private: 01255 smart_dir_node m_directory; 01256 const QString &m_prefix; 01257 VideoMetadataListManager::metadata_list &m_metalist; 01258 free_list &m_dh_free_list; 01259 const bool m_infer_title; 01260 }; 01261 01262 void VideoListImp::buildFileList(smart_dir_node &directory, 01263 metadata_list &metalist, const QString &prefix) 01264 { 01265 FileAssociations::ext_ignore_list ext_list; 01266 FileAssociations::getFileAssociation().getExtensionIgnoreList(ext_list); 01267 01268 dirhandler::free_list fl; 01269 dirhandler dh(directory, prefix, metalist, fl, false); 01270 (void) ScanVideoDirectory(directory->getFQPath(), &dh, ext_list, m_ListUnknown); 01271 }
1.7.6.1