MythTV  0.26-pre
videolist.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends