return connect(true);
}
+string Database::prepare(const char *format, ...)
+{
+ string result = "";
+
+ va_list args;
+ va_start(args, format);
+ result = vprepare(format, args);
+ va_end(args);
+
+ return result;
+}
+
+string Database::vprepare(const char *format, va_list args)
+{
+ char *p = NULL;
+ string result = "";
+
+ vsprintf(p, format, args);
+
+ if ( p )
+ {
+ result = p;
+ free(p);
+ }
+
+ return result;
+}
virtual void rollback_transaction() {};
/* virtual methods for formatting */
- virtual std::string vprepare(const char *format, va_list args) { return std::string(""); };
+
+ /*! \brief Prepare a SQL statement for execution or querying using C printf nomenclature.
+ \param format - C printf compliant format string
+ \param ... - optional comma seperated list of variables for substitution in format string placeholders.
+ \return escaped and formatted string.
+ */
+ virtual std::string prepare(const char *format, ...);
+
+ /*! \brief Prepare a SQL statement for execution or querying using C printf nomenclature
+ \param format - C printf compliant format string
+ \param args - va_list of variables for substitution in format string placeholders.
+ \return escaped and formatted string.
+ */
+ virtual std::string vprepare(const char *format, va_list args);
virtual bool in_transaction() {return false;};
/* Refresh dataset (reopen it and set the same cursor position) */
virtual void refresh();
+ /*! \brief Drop an index from the database table, provided it exists.
+ \param table - name of the table the index to be dropped is associated with
+ \param index - name of the index to be dropped
+ \return true when the index is guaranteed to no longer exist in the database.
+ */
+ virtual bool dropIndex(const char *table, const char *index) { return false; }
+
/* Go to record No (starting with 0) */
virtual bool seek(int pos=0);
/* Go to record No (starting with 1) */
//------------- public functions implementation -----------------//
-//FILE* file;
+bool MysqlDataset::dropIndex(const char *table, const char *index)
+{
+ string sql;
+ string sql_prepared;
+
+ sql = "SELECT * FROM information_schema.statistics WHERE TABLE_SCHEMA=DATABASE() AND table_name='%s' AND index_name='%s'";
+ sql_prepared = static_cast<MysqlDatabase*>(db)->prepare(sql.c_str(), table, index);
+
+ if (!query(sql_prepared))
+ return false;
+
+ if (num_rows())
+ {
+ sql = "ALTER TABLE %s DROP INDEX %s";
+ sql_prepared = static_cast<MysqlDatabase*>(db)->prepare(sql.c_str(), table, index);
+
+ if (exec(sql_prepared) != MYSQL_OK)
+ return false;
+ }
+
+ return true;
+}
+
int MysqlDataset::exec(const string &sql) {
if (!handle()) throw DbErrors("No Database Connection");
string qry = sql;
// enforce the "auto_increment" keyword to be appended to "integer primary key"
size_t loc;
+
if ( (loc=qry.find("integer primary key")) != string::npos)
{
qry = qry.insert(loc + 19, " auto_increment ");
{
qry += " CHARACTER SET utf8 COLLATE utf8_general_ci";
}
-
// sqlite3 requires the BEGIN and END pragmas when creating triggers. mysql does not.
- if ( qry.find("CREATE TRIGGER") != string::npos )
+ else if ( qry.find("CREATE TRIGGER") != string::npos )
{
if ( (loc=qry.find("BEGIN ")) != string::npos )
{
void mysqlStrAccumReset(StrAccum *p);
void mysqlStrAccumInit(StrAccum *p, char *zBase, int n, int mx);
char *mysql_vmprintf(const char *zFormat, va_list ap);
-
};
/* Go to record No (starting with 0) */
virtual bool seek(int pos=0);
+ virtual bool dropIndex(const char *table, const char *index);
};
} //namespace
#endif
//------------- public functions implementation -----------------//
+bool SqliteDataset::dropIndex(const char *table, const char *index)
+{
+ string sql;
+
+ sql = static_cast<SqliteDatabase*>(db)->prepare("DROP INDEX IF EXISTS %s", index);
+
+ return (exec(sql) == SQLITE_OK);
+}
+
int SqliteDataset::exec(const string &sql) {
if (!handle()) throw DbErrors("No Database Connection");
}
}
}
+ // Strip ON table from DROP INDEX statements:
+ // before: DROP INDEX foo ON table
+ // after: DROP INDEX foo
+ size_t pos = qry.find("DROP INDEX ");
+ if ( pos != string::npos )
+ {
+ pos = qry.find(" ON ", pos+1);
+
+ if ( pos != string::npos )
+ qry = qry.substr(0, pos);
+ }
if((res = db->setErr(sqlite3_exec(handle(),qry.c_str(),&callback,&exec_res,&errmsg),qry.c_str())) == SQLITE_OK)
return res;
/* Go to record No (starting with 0) */
virtual bool seek(int pos=0);
-
+ virtual bool dropIndex(const char *table, const char *index);
};
} //namespace
#endif
m_pDS->exec("CREATE UNIQUE INDEX ix_setlinkmovie_2 ON setlinkmovie ( idMovie, idSet)\n");
// create basepath indices
- m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c22(255) )");
- m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c13(255) )");
- m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c18(255) )");
- m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c16(255) )");
+ m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
+ m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
+ m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
+ m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c17(12) )");
}
catch (...)
{
CStdString strSQL;
try
{
- int idPath;
+ int idPath = GetPathId(strPath);
+ if (idPath >= 0)
+ return idPath; // already have the path
+
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
CStdString strFileName, strPath;
SplitPath(strFileNameAndPath,strPath,strFileName);
- int idPath=GetPathId(strPath);
- if (idPath < 0)
- idPath = AddPath(strPath);
-
+ int idPath = AddPath(strPath);
if (idPath < 0)
return -1;
if (!CDirectory::Exists(path))
return false;
}
- int idPath = GetPathId(path);
- if (idPath < 0)
- idPath = AddPath(path);
+ int idPath = AddPath(path);
if (idPath < 0) return false;
CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
m_pDS->exec(strSQL.c_str());
int idTvShow = (int)m_pDS->lastinsertid();
- int idPath = GetPathId(strPath);
- if (idPath < 0)
- idPath = AddPath(strPath);
+ int idPath = AddPath(strPath);
strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
m_pDS->exec(strSQL.c_str());
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
- int idPath = GetPathId(filePath);
+
+ int idPath = AddPath(filePath);
if (idPath < 0)
- { // no path found - we have to add one
- idPath = AddPath(filePath);
- if (idPath < 0) return ;
- }
+ return;
// Update
CStdString strSQL;
m_pDS->exec("ALTER TABLE actorlinktvshow ADD iOrder integer");
m_pDS->exec("ALTER TABLE actorlinkepisode ADD iOrder integer");
}
+ if (iVersion < 52)
+ { // Add basepath link to path table for faster content retrieval, and indicies
+ m_pDS->exec("ALTER table movie add c23 text");
+ m_pDS->exec("ALTER table episode add c23 text");
+ m_pDS->exec("ALTER table musicvideo add c23 text");
+ m_pDS->exec("ALTER table tvshow add c23 text");
+ m_pDS->dropIndex("movie", "ixMovieBasePath");
+ m_pDS->dropIndex("musicvideo", "ixMusicVideoBasePath");
+ m_pDS->dropIndex("episode", "ixEpisodeBasePath");
+ m_pDS->dropIndex("tvshow", "ixTVShowBasePath");
+ m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
+ m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
+ m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
+ m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c17(12) )");
+ // now update the base path links
+ UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
+ UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
+ UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
+ UpdateBasePathID("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, VIDEODB_ID_TV_PARENTPATHID);
+ }
}
catch (...)
{
return true;
}
+bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
+{
+ SScanSettings settings;
+ bool foundDirectly = false;
+ ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
+ if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
+ return false; // episodes
+ return settings.parent_name_root; // shows, movies, musicvids
+}
+
void CVideoDatabase::UpdateBasePath(const char *table, const char *id, int column, bool shows)
{
CStdString query;
map<CStdString, bool>::iterator i = paths.find(path);
if (i == paths.end())
{
- SScanSettings settings;
- bool foundDirectly = false;
- ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
- if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
- paths.insert(make_pair(path, false)); // episodes
- else
- paths.insert(make_pair(path, settings.parent_name_root)); // shows, movies, musicvids
+ paths.insert(make_pair(path, LookupByFolders(path, shows)));
i = paths.find(path);
}
CStdString filename;
m_pDS2->close();
}
+void CVideoDatabase::UpdateBasePathID(const char *table, const char *id, int column, int idColumn)
+{
+ CStdString query = PrepareSQL("SELECT %s,c%02d from %s", id, column, table);
+ m_pDS2->query(query.c_str());
+ while (!m_pDS2->eof())
+ {
+ int rowID = m_pDS2->fv(0).get_asInt();
+ CStdString path(m_pDS2->fv(1).get_asString());
+ // find the parent path of this item
+ int pathID = AddPath(URIUtils::GetParentPath(path));
+ if (pathID >= 0)
+ {
+ CStdString sql = PrepareSQL("UPDATE %s SET c%02d=%d WHERE %s=%d", table, idColumn, pathID, id, rowID);
+ m_pDS->exec(sql.c_str());
+ }
+ m_pDS2->next();
+ }
+ m_pDS2->close();
+}
+
+bool CVideoDatabase::GetPlayCounts(CFileItemList &items)
+{
+ int pathID = GetPathId(items.m_strPath);
+ if (pathID < 0)
+ return false; // path (and thus files) aren't in the database
+
+ try
+ {
+ // error!
+ if (NULL == m_pDB.get()) return false;
+ if (NULL == m_pDS.get()) return false;
+
+ // TODO: also test a single query for the above and below
+ CStdString sql = PrepareSQL("select strFilename,playCount from files where idPath=%i", pathID);
+ if (RunQuery(sql) <= 0)
+ return false;
+
+ items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
+ while (!m_pDS->eof())
+ {
+ CStdString path;
+ ConstructPath(path, items.m_strPath, m_pDS->fv(0).get_asString());
+ CFileItemPtr item = items.Get(path);
+ if (item)
+ item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
+ m_pDS->next();
+ }
+ return true;
+ }
+ catch (...)
+ {
+ CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
+ }
+ return false;
+}
+
int CVideoDatabase::GetPlayCount(const CFileItem &item)
{
int id = GetFileId(item);
CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
CVideoInfoScanner scanner;
set<CStdString> actors;
+ // add paths first (so we have scraper settings available)
+ TiXmlElement *path = root->FirstChildElement("paths");
+ path = path->FirstChildElement();
+ while (path)
+ {
+ CStdString strPath;
+ if (XMLUtils::GetString(path,"url",strPath))
+ AddPath(strPath);
+
+ CStdString content;
+ if (XMLUtils::GetString(path,"content", content))
+ { // check the scraper exists, if so store the path
+ AddonPtr addon;
+ CStdString uuid;
+ XMLUtils::GetString(path,"scraperID",uuid);
+ if (CAddonMgr::Get().GetAddon(uuid, addon))
+ {
+ SScanSettings settings;
+ ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
+ // FIXME: scraper settings are not exported?
+ scraper->SetPathSettings(TranslateContent(content), "");
+ XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
+ XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
+ SetScraperForPath(strPath,scraper,settings);
+ }
+ }
+ path = path->NextSiblingElement();
+ }
movie = root->FirstChildElement();
while (movie)
{
{
info.Load(movie);
CFileItem item(info);
- scanner.AddVideo(&item,CONTENT_MOVIES);
+ bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.m_strPath) : false;
+ scanner.AddVideo(&item, CONTENT_MOVIES, useFolders);
SetPlayCount(item, info.m_playCount, info.m_lastPlayed);
CStdString strFileName(info.m_strTitle);
if (GetExportVersion() >= 1 && info.m_iYear > 0)
{
info.Load(movie);
CFileItem item(info);
- scanner.AddVideo(&item,CONTENT_MUSICVIDEOS);
+ bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.m_strPath) : false;
+ scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders);
SetPlayCount(item, info.m_playCount, info.m_lastPlayed);
CStdString strFileName(info.m_strArtist + "." + info.m_strTitle);
if (GetExportVersion() >= 1 && info.m_iYear > 0)
URIUtils::AddSlashAtEnd(info.m_strPath);
DeleteTvShow(info.m_strPath);
CFileItem item(info);
- int showID = scanner.AddVideo(&item,CONTENT_TVSHOWS);
+ bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.m_strPath, true) : false;
+ int showID = scanner.AddVideo(&item, CONTENT_TVSHOWS, useFolders);
current++;
CStdString showDir(GetSafeFile(tvshowsDir, info.m_strTitle));
CFile::Cache(URIUtils::AddFileToFolder(showDir, "folder.jpg"), item.GetCachedVideoThumb());
// and fetch season thumbs
scanner.FetchSeasonThumbs(showID, showDir, false, true);
}
- else if (strnicmp(movie->Value(), "paths", 5) == 0)
- {
- const TiXmlElement* path = movie->FirstChildElement("path");
- while (path)
- {
- CStdString strPath;
- XMLUtils::GetString(path,"url",strPath);
- CStdString content;
-
- if (XMLUtils::GetString(path,"content", content))
- { // check the scraper exists, if so store the path
- AddonPtr addon;
- CStdString uuid;
-
- if (!XMLUtils::GetString(path,"scraperID",uuid))
- { // support pre addons exports
- XMLUtils::GetString(path, "scraperpath", uuid);
- uuid = URIUtils::GetFileName(uuid);
- }
-
- if (CAddonMgr::Get().GetAddon(uuid, addon))
- {
- SScanSettings settings;
- ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
- // FIXME: scraper settings are not exported?
- scraper->SetPathSettings(TranslateContent(content), "");
- XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
- XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
- SetScraperForPath(strPath,scraper,settings);
- }
- }
- path = path->NextSiblingElement();
- }
- }
movie = movie->NextSiblingElement();
if (progress && total)
{
}
return false;
}
+
+bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
+{
+ CStdString path(strPath);
+
+ if(URIUtils::IsMultiPath(path))
+ path = CMultiPathDirectory::GetFirstPath(path);
+
+ int pathID = GetPathId(path);
+ if (pathID < 0)
+ return false;
+
+ if (content == "movies")
+ {
+ CStdString where = PrepareSQL("where c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID);
+ GetMoviesByWhere("", where, "", items);
+ }
+ else if (content == "episodes")
+ {
+ CStdString where = PrepareSQL("where c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID);
+ GetEpisodesByWhere("", where, items);
+ }
+ else if (content == "tvshows")
+ {
+ CStdString where = PrepareSQL("where c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID);
+ GetTvShowsByWhere("", where, items);
+ }
+ else if (content == "musicvideos")
+ {
+ CStdString where = PrepareSQL("where c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID);
+ GetMusicVideosByWhere("", where, items);
+ }
+ for (int i = 0; i < items.Size(); i++)
+ items[i]->m_strPath = items[i]->GetVideoInfoTag()->m_basePath;
+ return items.Size() > 0;
+}
// these defines are based on how many columns we have and which column certain data is going to be in
// when we do GetDetailsForMovie()
-#define VIDEODB_MAX_COLUMNS 23
+#define VIDEODB_MAX_COLUMNS 24
#define VIDEODB_DETAILS_FILEID 1
#define VIDEODB_DETAILS_FILE VIDEODB_MAX_COLUMNS + 2
#define VIDEODB_DETAILS_PATH VIDEODB_MAX_COLUMNS + 3
VIDEODB_ID_FANART = 20,
VIDEODB_ID_COUNTRY = 21,
VIDEODB_ID_BASEPATH = 22,
+ VIDEODB_ID_PARENTPATHID = 23,
VIDEODB_ID_MAX
} VIDEODB_IDS;
{ VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_strTrailer) },
{ VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_fanart.m_xml) },
{ VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_strCountry) },
- { VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_basePath) }
+ { VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_basePath) },
+ { VIDEODB_TYPE_INT, my_offsetof(CVideoInfoTag,m_parentPathID) }
};
typedef enum // this enum MUST match the offset struct further down!! and make sure to keep min and max at -1 and sizeof(offsets)
VIDEODB_ID_TV_STUDIOS = 14,
VIDEODB_ID_TV_SORTTITLE = 15,
VIDEODB_ID_TV_BASEPATH = 16,
+ VIDEODB_ID_TV_PARENTPATHID = 17,
VIDEODB_ID_TV_MAX
} VIDEODB_TV_IDS;
{ VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_strMPAARating)},
{ VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_strStudio)},
{ VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_strSortTitle)},
- { VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_basePath) }
+ { VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_basePath) },
+ { VIDEODB_TYPE_INT, my_offsetof(CVideoInfoTag,m_parentPathID) }
};
typedef enum // this enum MUST match the offset struct further down!! and make sure to keep min and max at -1 and sizeof(offsets)
VIDEODB_ID_EPISODE_SORTEPISODE = 16,
VIDEODB_ID_EPISODE_BOOKMARK = 17,
VIDEODB_ID_EPISODE_BASEPATH = 18,
+ VIDEODB_ID_EPISODE_PARENTPATHID = 19,
VIDEODB_ID_EPISODE_MAX
} VIDEODB_EPISODE_IDS;
{ VIDEODB_TYPE_INT, my_offsetof(CVideoInfoTag,m_iSpecialSortSeason) },
{ VIDEODB_TYPE_INT, my_offsetof(CVideoInfoTag,m_iSpecialSortEpisode) },
{ VIDEODB_TYPE_INT, my_offsetof(CVideoInfoTag,m_iBookmarkId) },
- { VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_basePath) }
+ { VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_basePath) },
+ { VIDEODB_TYPE_INT, my_offsetof(CVideoInfoTag,m_parentPathID) }
};
typedef enum // this enum MUST match the offset struct further down!! and make sure to keep min and max at -1 and sizeof(offsets)
VIDEODB_ID_MUSICVIDEO_GENRE = 11,
VIDEODB_ID_MUSICVIDEO_TRACK = 12,
VIDEODB_ID_MUSICVIDEO_BASEPATH = 13,
+ VIDEODB_ID_MUSICVIDEO_PARENTPATHID = 14,
VIDEODB_ID_MUSICVIDEO_MAX
} VIDEODB_MUSICVIDEO_IDS;
{ VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_strArtist) },
{ VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_strGenre) },
{ VIDEODB_TYPE_INT, my_offsetof(CVideoInfoTag,m_iTrack) },
- { VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_basePath) }
+ { VIDEODB_TYPE_STRING, my_offsetof(CVideoInfoTag,m_basePath) },
+ { VIDEODB_TYPE_INT, my_offsetof(CVideoInfoTag,m_parentPathID) }
};
#define COMPARE_PERCENTAGE 0.90f // 90%
/*! \brief Increment the playcount of an item
Increments the playcount and updates the last played date
\param item CFileItem to increment the playcount for
- \sa GetPlayCount, SetPlayCount
+ \sa GetPlayCount, SetPlayCount, GetPlayCounts
*/
void IncrementPlayCount(const CFileItem &item);
/*! \brief Get the playcount of an item
\param item CFileItem to get the playcount for
\return the playcount of the item, or -1 on error
- \sa SetPlayCount, IncrementPlayCount
+ \sa SetPlayCount, IncrementPlayCount, GetPlayCounts
*/
int GetPlayCount(const CFileItem &item);
/*! \brief Update the last played time of an item
Updates the last played date
\param item CFileItem to update the last played time for
- \sa GetPlayCount, SetPlayCount, IncrementPlayCount
+ \sa GetPlayCount, SetPlayCount, IncrementPlayCount, GetPlayCounts
*/
void UpdateLastPlayed(const CFileItem &item);
+ /*! \brief Get the playcount of a list of items
+ \param items CFileItemList to fetch the playcounts for
+ \sa GetPlayCount, SetPlayCount, IncrementPlayCount
+ */
+ bool GetPlayCounts(CFileItemList &items);
+
void UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType=VIDEODB_CONTENT_MOVIES);
bool HasMovieInfo(const CStdString& strFilenameAndPath);
*/
bool GetItemForPath(const CStdString &content, const CStdString &path, CFileItem &item);
+ /*! \brief Get videos of the given content type from the given path
+ \param content the content type to fetch.
+ \param path the path to fetch videos from.
+ \param items the returned items
+ \return true if items are found, false otherwise.
+ */
+ bool GetItemsForPath(const CStdString &content, const CStdString &path, CFileItemList &items);
+
/*! \brief Check whether a given scraper is in use.
\param scraperID the scraper to check for.
\return true if the scraper is in use, false otherwise.
void CleanDatabase(VIDEO::IVideoInfoScannerObserver* pObserver=NULL, const std::vector<int>* paths=NULL);
/*! \brief Add a file to the database, if necessary
- If the file is already in the database, we simply return it's id.
+ If the file is already in the database, we simply return its id.
\param url - full path of the file to add.
\return id of the file, -1 if it could not be added.
*/
*/
int AddFile(const CFileItem& item);
+ /*! \brief Add a path to the database, if necessary
+ If the path is already in the database, we simply return its id.
+ \param strPath the path to add
+ \return id of the file, -1 if it could not be added.
+ */
+ int AddPath(const CStdString& strPath);
+
void ExportToXML(const CStdString &path, bool singleFiles = false, bool images=false, bool actorThumbs=false, bool overwrite=false);
bool ExportSkipEntry(const CStdString &nfoFile);
void ExportActorThumbs(const CStdString &path, const CVideoInfoTag& tag, bool singleFiles, bool overwrite=false);
*/
int GetFileId(const CStdString& url);
- int AddPath(const CStdString& strPath);
int AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value);
int AddGenre(const CStdString& strGenre1);
int AddActor(const CStdString& strActor, const CStdString& strThumb);
*/
void UpdateBasePath(const char *table, const char *id, int column, bool shows = false);
- virtual int GetMinVersion() const { return 51; };
+ /*! \brief Update routine for base path id of videos
+ Only required for videodb version < 52
+ \param table the table to update
+ \param id the primary id in the given table
+ \param column the column of the basepath
+ \param idColumn the column of the parent path id to update
+ */
+ void UpdateBasePathID(const char *table, const char *id, int column, int idColumn);
+
+ /*! \brief Determine whether the path is using lookup using folders
+ \param path the path to check
+ \param shows whether this path is from a tvshow (defaults to false)
+ */
+ bool LookupByFolders(const CStdString &path, bool shows = false);
+
+ virtual int GetMinVersion() const { return 52; };
virtual int GetExportVersion() const { return 1; };
const char *GetBaseDBName() const { return "MyVideos"; };
long lResult = -1;
CVideoInfoTag &movieDetails = *pItem->GetVideoInfoTag();
- movieDetails.m_basePath = pItem->GetBaseMoviePath(videoFolder);
+ if (movieDetails.m_basePath.IsEmpty())
+ movieDetails.m_basePath = pItem->GetBaseMoviePath(videoFolder);
+ movieDetails.m_parentPathID = m_database.AddPath(URIUtils::GetParentPath(movieDetails.m_basePath));
if (content == CONTENT_MOVIES)
{
m_playCount = 0;
m_fEpBookmark = 0;
m_basePath = "";
+ m_parentPathID = -1;
}
bool CVideoInfoTag::Save(TiXmlNode *node, const CStdString &tag, bool savePathInfo)
ar << m_strShowLink;
ar << m_fEpBookmark;
ar << m_basePath;
+ ar << m_parentPathID;
}
else
{
ar >> m_strShowLink;
ar >> m_fEpBookmark;
ar >> m_basePath;
+ ar >> m_parentPathID;
}
}
bool IsEmpty() const;
CStdString m_basePath; // the base path of the video, for folder-based lookups
+ int m_parentPathID; // the parent path id where the base path of the video lies
CStdString m_strDirector;
CStdString m_strWritingCredits;
CStdString m_strGenre;
!items.IsVirtualDirectoryRoot() &&
m_stackingAvailable);
+ CFileItemList dbItems;
+ if (content.IsEmpty())
+ m_database.GetPlayCounts(items);
+ else
+ {
+ m_database.GetItemsForPath(content, items.m_strPath, dbItems);
+ dbItems.SetFastLookup(true);
+ }
for (int i = 0; i < items.Size(); i++)
{
CFileItemPtr pItem = items[i];
- CFileItem item;
- if (!content.IsEmpty() && m_database.GetItemForPath(content, pItem->m_strPath, item))
- { // copy info across
+ CFileItemPtr match;
+ if (!content.IsEmpty())
+ match = dbItems.Get(pItem->m_strPath);
+ if (match)
+ {
CStdString label (pItem->GetLabel ());
CStdString label2(pItem->GetLabel2());
- pItem->UpdateInfo(item);
-
+ pItem->UpdateInfo(*match);
+
if(g_settings.m_videoStacking && m_stackingAvailable)
{
- pItem->m_strPath = item.m_strPath;
+ if (match->m_bIsFolder)
+ pItem->m_strPath = match->GetVideoInfoTag()->m_strPath;
+ else
+ pItem->m_strPath = match->GetVideoInfoTag()->m_strFileNameAndPath;
// if we switch from a file to a folder item it means we really shouldn't be sorting files and
// folders separately
- if (pItem->m_bIsFolder != item.m_bIsFolder)
+ if (pItem->m_bIsFolder != match->m_bIsFolder)
items.SetSortIgnoreFolders(true);
- pItem->m_bIsFolder = item.m_bIsFolder;
+ pItem->m_bIsFolder = match->m_bIsFolder;
}
else
{
- if (CFile::Exists(item.GetCachedFanart()))
- pItem->SetProperty("fanart_image", item.GetCachedFanart());
+ if (CFile::Exists(match->GetCachedFanart()))
+ pItem->SetProperty("fanart_image", match->GetCachedFanart());
pItem->SetLabel (label);
pItem->SetLabel2(label);
}
-
}
else
- { // grab the playcount and clean the label
- int playCount = m_database.GetPlayCount(*pItem);
- if (playCount >= 0)
- pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, playCount > 0);
+ { // set the watched overlay (note: items in a folder with content set that aren't in the db
+ // won't get picked up here - in the future all items will be returned)
+ // and clean the label
+ if (pItem->HasVideoInfoTag())
+ pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, pItem->GetVideoInfoTag()->m_playCount > 0);
if (clean)
pItem->CleanString();
}