2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "ApplicationMessenger.h"
22 #include "threads/SystemClock.h"
23 #include "VideoDatabase.h"
24 #include "video/windows/GUIWindowVideoBase.h"
25 #include "utils/RegExp.h"
26 #include "addons/AddonManager.h"
27 #include "GUIInfoManager.h"
29 #include "utils/URIUtils.h"
30 #include "utils/XMLUtils.h"
31 #include "GUIPassword.h"
32 #include "filesystem/StackDirectory.h"
33 #include "filesystem/MultiPathDirectory.h"
34 #include "VideoInfoScanner.h"
35 #include "guilib/GUIWindowManager.h"
36 #include "filesystem/Directory.h"
37 #include "filesystem/File.h"
38 #include "filesystem/SpecialProtocol.h"
39 #include "dialogs/GUIDialogExtendedProgressBar.h"
40 #include "dialogs/GUIDialogProgress.h"
41 #include "dialogs/GUIDialogYesNo.h"
43 #include "profiles/ProfilesManager.h"
44 #include "settings/AdvancedSettings.h"
45 #include "settings/MediaSettings.h"
46 #include "settings/MediaSourceSettings.h"
47 #include "settings/Settings.h"
48 #include "utils/StringUtils.h"
49 #include "guilib/LocalizeStrings.h"
50 #include "utils/TimeUtils.h"
51 #include "utils/log.h"
52 #include "TextureCache.h"
53 #include "addons/AddonInstaller.h"
54 #include "interfaces/AnnouncementManager.h"
55 #include "dbwrappers/dataset.h"
56 #include "utils/LabelFormatter.h"
57 #include "XBDateTime.h"
59 #include "video/VideoDbUrl.h"
60 #include "playlists/SmartPlayList.h"
61 #include "utils/GroupUtils.h"
64 using namespace dbiplus;
65 using namespace XFILE;
66 using namespace VIDEO;
67 using namespace ADDON;
69 //********************************************************************************************************************************
70 CVideoDatabase::CVideoDatabase(void)
74 //********************************************************************************************************************************
75 CVideoDatabase::~CVideoDatabase(void)
78 //********************************************************************************************************************************
79 bool CVideoDatabase::Open()
81 return CDatabase::Open(g_advancedSettings.m_databaseVideo);
84 void CVideoDatabase::CreateTables()
86 CLog::Log(LOGINFO, "create bookmark table");
87 m_pDS->exec("CREATE TABLE bookmark ( idBookmark integer primary key, idFile integer, timeInSeconds double, totalTimeInSeconds double, thumbNailImage text, player text, playerState text, type integer)\n");
89 CLog::Log(LOGINFO, "create settings table");
90 m_pDS->exec("CREATE TABLE settings ( idFile integer, Deinterlace bool,"
91 "ViewMode integer,ZoomAmount float, PixelRatio float, VerticalShift float, AudioStream integer, SubtitleStream integer,"
92 "SubtitleDelay float, SubtitlesOn bool, Brightness float, Contrast float, Gamma float,"
93 "VolumeAmplification float, AudioDelay float, OutputToAllSpeakers bool, ResumeTime integer, Crop bool, CropLeft integer,"
94 "CropRight integer, CropTop integer, CropBottom integer, Sharpness float, NoiseReduction float, NonLinStretch bool, PostProcess bool,"
95 "ScalingMethod integer, DeinterlaceMode integer, StereoMode integer, StereoInvert bool)\n");
97 CLog::Log(LOGINFO, "create stacktimes table");
98 m_pDS->exec("CREATE TABLE stacktimes (idFile integer, times text)\n");
100 CLog::Log(LOGINFO, "create genre table");
101 m_pDS->exec("CREATE TABLE genre ( idGenre integer primary key, strGenre text)\n");
103 CLog::Log(LOGINFO, "create genrelinkmovie table");
104 m_pDS->exec("CREATE TABLE genrelinkmovie ( idGenre integer, idMovie integer)\n");
106 CLog::Log(LOGINFO, "create country table");
107 m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
109 CLog::Log(LOGINFO, "create countrylinkmovie table");
110 m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
112 CLog::Log(LOGINFO, "create movie table");
113 CStdString columns = "CREATE TABLE movie ( idMovie integer primary key, idFile integer";
115 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
116 columns += StringUtils::Format(",c%02d text", i);
118 columns += ", idSet integer)";
119 m_pDS->exec(columns.c_str());
121 CLog::Log(LOGINFO, "create actorlinkmovie table");
122 m_pDS->exec("CREATE TABLE actorlinkmovie ( idActor integer, idMovie integer, strRole text, iOrder integer)\n");
124 CLog::Log(LOGINFO, "create directorlinkmovie table");
125 m_pDS->exec("CREATE TABLE directorlinkmovie ( idDirector integer, idMovie integer)\n");
127 CLog::Log(LOGINFO, "create writerlinkmovie table");
128 m_pDS->exec("CREATE TABLE writerlinkmovie ( idWriter integer, idMovie integer)\n");
130 CLog::Log(LOGINFO, "create actors table");
131 m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
133 CLog::Log(LOGINFO, "create path table");
134 m_pDS->exec("CREATE TABLE path ( idPath integer primary key, strPath text, strContent text, strScraper text, strHash text, scanRecursive integer, useFolderNames bool, strSettings text, noUpdate bool, exclude bool, dateAdded text)");
136 CLog::Log(LOGINFO, "create files table");
137 m_pDS->exec("CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text, dateAdded text)");
139 CLog::Log(LOGINFO, "create tvshow table");
140 columns = "CREATE TABLE tvshow ( idShow integer primary key";
142 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
143 columns += StringUtils::Format(",c%02d text", i);;
146 m_pDS->exec(columns.c_str());
148 CLog::Log(LOGINFO, "create directorlinktvshow table");
149 m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
151 CLog::Log(LOGINFO, "create actorlinktvshow table");
152 m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text, iOrder integer)\n");
154 CLog::Log(LOGINFO, "create studiolinktvshow table");
155 m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
157 CLog::Log(LOGINFO, "create episode table");
158 columns = "CREATE TABLE episode ( idEpisode integer primary key, idFile integer";
159 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
162 if ( i == VIDEODB_ID_EPISODE_SEASON || i == VIDEODB_ID_EPISODE_EPISODE || i == VIDEODB_ID_EPISODE_BOOKMARK)
163 column = StringUtils::Format(",c%02d varchar(24)", i);
165 column = StringUtils::Format(",c%02d text", i);
169 columns += ", idShow integer)";
170 m_pDS->exec(columns.c_str());
172 CLog::Log(LOGINFO, "create tvshowlinkpath table");
173 m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
175 CLog::Log(LOGINFO, "create actorlinkepisode table");
176 m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text, iOrder integer)\n");
178 CLog::Log(LOGINFO, "create directorlinkepisode table");
179 m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
181 CLog::Log(LOGINFO, "create writerlinkepisode table");
182 m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
184 CLog::Log(LOGINFO, "create genrelinktvshow table");
185 m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
187 CLog::Log(LOGINFO, "create movielinktvshow table");
188 m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
190 CLog::Log(LOGINFO, "create studio table");
191 m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
193 CLog::Log(LOGINFO, "create studiolinkmovie table");
194 m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
196 CLog::Log(LOGINFO, "create musicvideo table");
197 columns = "CREATE TABLE musicvideo ( idMVideo integer primary key, idFile integer";
198 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
199 columns += StringUtils::Format(",c%02d text", i);;
202 m_pDS->exec(columns.c_str());
204 CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
205 m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
207 CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
208 m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
210 CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
211 m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
213 CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
214 m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
216 CLog::Log(LOGINFO, "create streaminfo table");
217 m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
218 "strVideoCodec text, fVideoAspect float, iVideoWidth integer, iVideoHeight integer, "
219 "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text, iVideoDuration integer, strStereoMode text)");
221 CLog::Log(LOGINFO, "create sets table");
222 m_pDS->exec("CREATE TABLE sets ( idSet integer primary key, strSet text)\n");
224 CLog::Log(LOGINFO, "create seasons table");
225 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
227 CLog::Log(LOGINFO, "create art table");
228 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
230 CLog::Log(LOGINFO, "create tag table");
231 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
233 CLog::Log(LOGINFO, "create taglinks table");
234 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
237 void CVideoDatabase::CreateAnalytics()
239 /* indexes should be added on any columns that are used in in */
240 /* a where or a join. primary key on a column is the same as a */
241 /* unique index on that column, so there is no need to add any */
242 /* index if no other columns are refered */
244 /* order of indexes are important, for an index to be considered all */
245 /* columns up to the column in question have to have been specified */
246 /* select * from actorlinkmovie where idMovie = 1, can not take */
247 /* advantage of a index that has been created on ( idGenre, idMovie ) */
248 /*, hower on on ( idMovie, idGenre ) will be considered for use */
250 CLog::Log(LOGINFO, "%s - creating indicies", __FUNCTION__);
251 m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
252 m_pDS->exec("CREATE UNIQUE INDEX ix_settings ON settings ( idFile )\n");
253 m_pDS->exec("CREATE UNIQUE INDEX ix_stacktimes ON stacktimes ( idFile )\n");
254 m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
255 m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
257 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_1 ON genrelinkmovie ( idGenre, idMovie)\n");
258 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_2 ON genrelinkmovie ( idMovie, idGenre)\n");
259 m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
260 m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
261 m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
262 m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
263 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_1 ON actorlinkmovie ( idActor, idMovie )\n");
264 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_2 ON actorlinkmovie ( idMovie, idActor )\n");
265 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_1 ON directorlinkmovie ( idDirector, idMovie )\n");
266 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_2 ON directorlinkmovie ( idMovie, idDirector )\n");
267 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_1 ON writerlinkmovie ( idWriter, idMovie )\n");
268 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_2 ON writerlinkmovie ( idMovie, idWriter )\n");
269 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
270 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
272 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
273 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
274 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
275 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
276 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
277 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
278 m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
279 m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
280 m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
281 m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
283 m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
284 m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
285 CStdString createColIndex = StringUtils::Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
286 m_pDS->exec(createColIndex.c_str());
287 createColIndex = StringUtils::Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
288 m_pDS->exec(createColIndex.c_str());
289 m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
290 m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
291 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
292 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
293 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
294 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
295 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
296 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
297 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
298 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
300 m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
301 m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
302 m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
303 m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
304 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
305 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
306 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
307 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
308 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
309 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
311 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
312 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
313 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
314 m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c17(12) )");
316 m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
317 m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
318 m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
320 m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
321 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
322 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
323 m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
325 CLog::Log(LOGINFO, "%s - creating triggers", __FUNCTION__);
326 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
327 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
328 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
330 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
331 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
332 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
334 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
335 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
336 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
338 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
339 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
341 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
342 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
344 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
345 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
347 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
348 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
350 m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
351 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
357 void CVideoDatabase::CreateViews()
359 CLog::Log(LOGINFO, "create episodeview");
360 CStdString episodeview = PrepareSQL("CREATE VIEW episodeview AS SELECT "
362 " files.strFileName AS strFileName,"
363 " path.strPath AS strPath,"
364 " files.playCount AS playCount,"
365 " files.lastPlayed AS lastPlayed,"
366 " files.dateAdded AS dateAdded,"
367 " tvshow.c%02d AS strTitle,"
368 " tvshow.c%02d AS strStudio,"
369 " tvshow.c%02d AS premiered,"
370 " tvshow.c%02d AS mpaa,"
371 " tvshow.c%02d AS strShowPath, "
372 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
373 " bookmark.totalTimeInSeconds AS totalTimeInSeconds, "
374 " seasons.idSeason AS idSeason "
377 " files.idFile=episode.idFile"
379 " tvshow.idShow=episode.idShow"
380 " LEFT JOIN seasons ON"
381 " seasons.idShow=episode.idShow AND seasons.season=episode.c%02d"
383 " files.idPath=path.idPath"
384 " LEFT JOIN bookmark ON"
385 " bookmark.idFile=episode.idFile AND bookmark.type=1", VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_PREMIERED, VIDEODB_ID_TV_MPAA, VIDEODB_ID_TV_BASEPATH, VIDEODB_ID_EPISODE_SEASON);
386 m_pDS->exec(episodeview.c_str());
388 CLog::Log(LOGINFO, "create tvshowview");
389 CStdString tvshowview = PrepareSQL("CREATE VIEW tvshowview AS SELECT "
391 " path.strPath AS strPath,"
392 " path.dateAdded AS dateAdded,"
393 " MAX(files.lastPlayed) AS lastPlayed,"
394 " NULLIF(COUNT(episode.c12), 0) AS totalCount,"
395 " COUNT(files.playCount) AS watchedcount,"
396 " NULLIF(COUNT(DISTINCT(episode.c12)), 0) AS totalSeasons "
398 " LEFT JOIN tvshowlinkpath ON"
399 " tvshowlinkpath.idShow=tvshow.idShow"
401 " path.idPath=tvshowlinkpath.idPath"
402 " LEFT JOIN episode ON"
403 " episode.idShow=tvshow.idShow"
404 " LEFT JOIN files ON"
405 " files.idFile=episode.idFile "
406 "GROUP BY tvshow.idShow;");
407 m_pDS->exec(tvshowview.c_str());
409 CLog::Log(LOGINFO, "create musicvideoview");
410 m_pDS->exec("CREATE VIEW musicvideoview AS SELECT"
412 " files.strFileName as strFileName,"
413 " path.strPath as strPath,"
414 " files.playCount as playCount,"
415 " files.lastPlayed as lastPlayed,"
416 " files.dateAdded as dateAdded, "
417 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
418 " bookmark.totalTimeInSeconds AS totalTimeInSeconds "
421 " files.idFile=musicvideo.idFile"
423 " path.idPath=files.idPath"
424 " LEFT JOIN bookmark ON"
425 " bookmark.idFile=musicvideo.idFile AND bookmark.type=1");
427 CLog::Log(LOGINFO, "create movieview");
428 m_pDS->exec("CREATE VIEW movieview AS SELECT"
430 " sets.strSet AS strSet,"
431 " files.strFileName AS strFileName,"
432 " path.strPath AS strPath,"
433 " files.playCount AS playCount,"
434 " files.lastPlayed AS lastPlayed, "
435 " files.dateAdded AS dateAdded, "
436 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
437 " bookmark.totalTimeInSeconds AS totalTimeInSeconds "
440 " sets.idSet = movie.idSet"
442 " files.idFile=movie.idFile"
444 " path.idPath=files.idPath"
445 " LEFT JOIN bookmark ON"
446 " bookmark.idFile=movie.idFile AND bookmark.type=1");
449 //********************************************************************************************************************************
450 int CVideoDatabase::GetPathId(const CStdString& strPath)
456 if (NULL == m_pDB.get()) return -1;
457 if (NULL == m_pDS.get()) return -1;
459 CStdString strPath1(strPath);
460 if (URIUtils::IsStack(strPath) || StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://"))
461 URIUtils::GetParentPath(strPath,strPath1);
463 URIUtils::AddSlashAtEnd(strPath1);
465 strSQL=PrepareSQL("select idPath from path where strPath='%s'",strPath1.c_str());
466 m_pDS->query(strSQL.c_str());
468 idPath = m_pDS->fv("path.idPath").get_asInt();
475 CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
480 bool CVideoDatabase::GetPaths(set<CStdString> &paths)
484 if (NULL == m_pDB.get()) return false;
485 if (NULL == m_pDS.get()) return false;
489 // grab all paths with movie content set
490 if (!m_pDS->query("select strPath,noUpdate from path"
491 " where (strContent = 'movies' or strContent = 'musicvideos')"
492 " and strPath NOT like 'multipath://%%'"
493 " order by strPath"))
496 while (!m_pDS->eof())
498 if (!m_pDS->fv("noUpdate").get_asBool())
499 paths.insert(m_pDS->fv("strPath").get_asString());
504 // then grab all tvshow paths
505 if (!m_pDS->query("select strPath,noUpdate from path"
506 " where ( strContent = 'tvshows'"
507 " or idPath in (select idPath from tvshowlinkpath))"
508 " and strPath NOT like 'multipath://%%'"
509 " order by strPath"))
512 while (!m_pDS->eof())
514 if (!m_pDS->fv("noUpdate").get_asBool())
515 paths.insert(m_pDS->fv("strPath").get_asString());
520 // finally grab all other paths holding a movie which is not a stack or a rar archive
521 // - this isnt perfect but it should do fine in most situations.
522 // reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
523 // not making mistakes must take priority
524 if (!m_pDS->query("select strPath,noUpdate from path"
525 " where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
526 " and idPath NOT in (select idPath from tvshowlinkpath)"
527 " and idPath NOT in (select idPath from files where strFileName like 'video_ts.ifo')" // dvd folders get stacked to a single item in parent folder
528 " and idPath NOT in (select idPath from files where strFileName like 'index.bdmv')" // bluray folders get stacked to a single item in parent folder
529 " and strPath NOT like 'multipath://%%'"
530 " and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
531 " order by strPath"))
534 while (!m_pDS->eof())
536 if (!m_pDS->fv("noUpdate").get_asBool())
537 paths.insert(m_pDS->fv("strPath").get_asString());
545 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
550 bool CVideoDatabase::GetPathsForTvShow(int idShow, set<int>& paths)
555 if (NULL == m_pDB.get()) return false;
556 if (NULL == m_pDS.get()) return false;
557 strSQL = PrepareSQL("SELECT DISTINCT idPath FROM files JOIN episode ON episode.idFile=files.idFile WHERE episode.idShow=%i",idShow);
558 m_pDS->query(strSQL.c_str());
559 while (!m_pDS->eof())
561 paths.insert(m_pDS->fv(0).get_asInt());
569 CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
574 int CVideoDatabase::RunQuery(const CStdString &sql)
576 unsigned int time = XbmcThreads::SystemClockMillis();
578 if (m_pDS->query(sql.c_str()))
580 rows = m_pDS->num_rows();
584 CLog::Log(LOGDEBUG, "%s took %d ms for %d items query: %s", __FUNCTION__, XbmcThreads::SystemClockMillis() - time, rows, sql.c_str());
588 bool CVideoDatabase::GetSubPaths(const CStdString &basepath, vector< pair<int,string> >& subpaths)
593 if (!m_pDB.get() || !m_pDS.get())
596 CStdString path(basepath);
597 URIUtils::AddSlashAtEnd(path);
598 sql = PrepareSQL("SELECT idPath,strPath FROM path WHERE SUBSTR(strPath,1,%i)='%s'", StringUtils::utf8_strlen(path.c_str()), path.c_str());
599 m_pDS->query(sql.c_str());
600 while (!m_pDS->eof())
602 subpaths.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
610 CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, sql.c_str());
615 int CVideoDatabase::AddPath(const CStdString& strPath, const CStdString &strDateAdded /*= "" */)
620 int idPath = GetPathId(strPath);
622 return idPath; // already have the path
624 if (NULL == m_pDB.get()) return -1;
625 if (NULL == m_pDS.get()) return -1;
627 CStdString strPath1(strPath);
628 if (URIUtils::IsStack(strPath) || StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://"))
629 URIUtils::GetParentPath(strPath,strPath1);
631 URIUtils::AddSlashAtEnd(strPath1);
633 // only set dateadded if we got one
634 if (!strDateAdded.empty())
635 strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper, dateAdded) values (NULL,'%s','','', '%s')", strPath1.c_str(), strDateAdded.c_str());
637 strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
638 m_pDS->exec(strSQL.c_str());
639 idPath = (int)m_pDS->lastinsertid();
644 CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
649 bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
653 if (NULL == m_pDB.get()) return false;
654 if (NULL == m_pDS.get()) return false;
656 CStdString strSQL=PrepareSQL("select strHash from path where strPath='%s'", path.c_str());
657 m_pDS->query(strSQL.c_str());
658 if (m_pDS->num_rows() == 0)
660 hash = m_pDS->fv("strHash").get_asString();
665 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
671 //********************************************************************************************************************************
672 int CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
674 CStdString strSQL = "";
678 if (NULL == m_pDB.get()) return -1;
679 if (NULL == m_pDS.get()) return -1;
681 CStdString strFileName, strPath;
682 SplitPath(strFileNameAndPath,strPath,strFileName);
684 int idPath = AddPath(strPath);
688 CStdString strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
690 m_pDS->query(strSQL.c_str());
691 if (m_pDS->num_rows() > 0)
693 idFile = m_pDS->fv("idFile").get_asInt() ;
699 strSQL=PrepareSQL("insert into files (idFile, idPath, strFileName) values(NULL, %i, '%s')", idPath, strFileName.c_str());
700 m_pDS->exec(strSQL.c_str());
701 idFile = (int)m_pDS->lastinsertid();
706 CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
711 int CVideoDatabase::AddFile(const CFileItem& item)
713 if (item.IsVideoDb() && item.HasVideoInfoTag())
714 return AddFile(item.GetVideoInfoTag()->m_strFileNameAndPath);
715 return AddFile(item.GetPath());
718 void CVideoDatabase::UpdateFileDateAdded(int idFile, const CStdString& strFileNameAndPath)
720 if (idFile < 0 || strFileNameAndPath.empty())
723 CStdString strSQL = "";
726 if (NULL == m_pDB.get()) return;
727 if (NULL == m_pDS.get()) return;
729 CStdString file = strFileNameAndPath;
730 if (URIUtils::IsStack(strFileNameAndPath))
731 file = CStackDirectory::GetFirstStackedFile(strFileNameAndPath);
733 if (URIUtils::IsInArchive(file))
734 file = CURL(file).GetHostName();
737 // Skip looking at the files ctime/mtime if defined by the user through as.xml
738 if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
740 // Let's try to get the modification datetime
741 struct __stat64 buffer;
742 if (CFile::Stat(file, &buffer) == 0 && (buffer.st_mtime != 0 || buffer.st_ctime !=0))
744 time_t now = time(NULL);
746 // Prefer the modification time if it's valid
747 if (g_advancedSettings.m_iVideoLibraryDateAdded == 1)
749 if (buffer.st_mtime != 0 && (time_t)buffer.st_mtime <= now)
750 addedTime = (time_t)buffer.st_mtime;
752 addedTime = (time_t)buffer.st_ctime;
754 // Use the newer of the creation and modification time
757 addedTime = max((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
758 // if the newer of the two dates is in the future, we try it with the older one
760 addedTime = min((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
763 // make sure the datetime does is not in the future
764 if (addedTime <= now)
766 struct tm *time = localtime(&addedTime);
773 if (!dateAdded.IsValid())
774 dateAdded = CDateTime::GetCurrentDateTime();
776 strSQL = PrepareSQL("update files set dateAdded='%s' where idFile=%d", dateAdded.GetAsDBDateTime().c_str(), idFile);
777 m_pDS->exec(strSQL.c_str());
781 CLog::Log(LOGERROR, "%s unable to update dateadded for file (%s)", __FUNCTION__, strSQL.c_str());
785 bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
789 if (NULL == m_pDB.get()) return false;
790 if (NULL == m_pDS.get()) return false;
792 int idPath = AddPath(path);
793 if (idPath < 0) return false;
795 CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
796 m_pDS->exec(strSQL.c_str());
802 CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
808 bool CVideoDatabase::LinkMovieToTvshow(int idMovie, int idShow, bool bRemove)
812 if (NULL == m_pDB.get()) return false;
813 if (NULL == m_pDS.get()) return false;
815 if (bRemove) // delete link
817 CStdString strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i and idShow=%i", idMovie, idShow);
818 m_pDS->exec(strSQL.c_str());
822 CStdString strSQL=PrepareSQL("insert into movielinktvshow (idShow,idMovie) values (%i,%i)", idShow,idMovie);
823 m_pDS->exec(strSQL.c_str());
829 CLog::Log(LOGERROR, "%s (%i, %i) failed", __FUNCTION__, idMovie, idShow);
835 bool CVideoDatabase::IsLinkedToTvshow(int idMovie)
839 if (NULL == m_pDB.get()) return false;
840 if (NULL == m_pDS.get()) return false;
842 CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
843 m_pDS->query(strSQL.c_str());
855 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
861 bool CVideoDatabase::GetLinksToTvShow(int idMovie, vector<int>& ids)
865 if (NULL == m_pDB.get()) return false;
866 if (NULL == m_pDS.get()) return false;
868 CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
869 m_pDS2->query(strSQL.c_str());
870 while (!m_pDS2->eof())
872 ids.push_back(m_pDS2->fv(1).get_asInt());
881 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
888 //********************************************************************************************************************************
889 int CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
893 if (NULL == m_pDB.get()) return -1;
894 if (NULL == m_pDS.get()) return -1;
895 CStdString strPath, strFileName;
896 SplitPath(strFilenameAndPath,strPath,strFileName);
898 int idPath = GetPathId(strPath);
902 strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
903 m_pDS->query(strSQL.c_str());
904 if (m_pDS->num_rows() > 0)
906 int idFile = m_pDS->fv("files.idFile").get_asInt();
914 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
919 int CVideoDatabase::GetFileId(const CFileItem &item)
921 if (item.IsVideoDb() && item.HasVideoInfoTag())
922 return GetFileId(item.GetVideoInfoTag()->m_strFileNameAndPath);
923 return GetFileId(item.GetPath());
926 //********************************************************************************************************************************
927 int CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
931 if (NULL == m_pDB.get()) return -1;
932 if (NULL == m_pDS.get()) return -1;
935 // needed for query parameters
936 int idFile = GetFileId(strFilenameAndPath);
942 SplitPath(strFilenameAndPath,strPath,strFile);
944 // have to join movieinfo table for correct results
945 idPath = GetPathId(strPath);
946 if (idPath < 0 && strPath != strFilenameAndPath)
950 if (idFile == -1 && strPath != strFilenameAndPath)
955 strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
957 strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
959 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
960 m_pDS->query(strSQL.c_str());
961 if (m_pDS->num_rows() > 0)
962 idMovie = m_pDS->fv("idMovie").get_asInt();
969 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
974 int CVideoDatabase::GetTvShowId(const CStdString& strPath)
978 if (NULL == m_pDB.get()) return -1;
979 if (NULL == m_pDS.get()) return -1;
982 // have to join movieinfo table for correct results
983 int idPath = GetPathId(strPath);
988 CStdString strPath1=strPath;
989 CStdString strParent;
992 strSQL=PrepareSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%i",idPath);
993 m_pDS->query(strSQL);
997 while (iFound == 0 && URIUtils::GetParentPath(strPath1, strParent))
999 strSQL=PrepareSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and strPath='%s'",strParent.c_str());
1000 m_pDS->query(strSQL.c_str());
1003 int idShow = m_pDS->fv("idShow").get_asInt();
1007 strPath1 = strParent;
1010 if (m_pDS->num_rows() > 0)
1011 idTvShow = m_pDS->fv("idShow").get_asInt();
1018 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1023 int CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, int idEpisode, int idSeason) // input value is episode/season number hint - for multiparters
1027 if (NULL == m_pDB.get()) return -1;
1028 if (NULL == m_pDS.get()) return -1;
1030 // need this due to the nested GetEpisodeInfo query
1031 auto_ptr<Dataset> pDS;
1032 pDS.reset(m_pDB->CreateDataset());
1033 if (NULL == pDS.get()) return -1;
1035 int idFile = GetFileId(strFilenameAndPath);
1039 CStdString strSQL=PrepareSQL("select idEpisode from episode where idFile=%i", idFile);
1041 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
1042 pDS->query(strSQL.c_str());
1043 if (pDS->num_rows() > 0)
1045 if (idEpisode == -1)
1046 idEpisode = pDS->fv("episode.idEpisode").get_asInt();
1047 else // use the hint!
1052 int idTmpEpisode = pDS->fv("episode.idEpisode").get_asInt();
1053 GetEpisodeInfo(strFilenameAndPath,tag,idTmpEpisode);
1054 if (tag.m_iEpisode == idEpisode && (idSeason == -1 || tag.m_iSeason == idSeason)) {
1055 // match on the episode hint, and there's no season hint or a season hint match
1056 idEpisode = idTmpEpisode;
1074 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1079 int CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
1083 if (NULL == m_pDB.get()) return -1;
1084 if (NULL == m_pDS.get()) return -1;
1086 int idFile = GetFileId(strFilenameAndPath);
1090 CStdString strSQL=PrepareSQL("select idMVideo from musicvideo where idFile=%i", idFile);
1092 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
1093 m_pDS->query(strSQL.c_str());
1095 if (m_pDS->num_rows() > 0)
1096 idMVideo = m_pDS->fv("idMVideo").get_asInt();
1103 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1108 //********************************************************************************************************************************
1109 int CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
1113 if (NULL == m_pDB.get()) return -1;
1114 if (NULL == m_pDS.get()) return -1;
1116 int idMovie = GetMovieId(strFilenameAndPath);
1119 int idFile = AddFile(strFilenameAndPath);
1122 UpdateFileDateAdded(idFile, strFilenameAndPath);
1123 CStdString strSQL=PrepareSQL("insert into movie (idMovie, idFile) values (NULL, %i)", idFile);
1124 m_pDS->exec(strSQL.c_str());
1125 idMovie = (int)m_pDS->lastinsertid();
1132 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1137 int CVideoDatabase::AddTvShow(const CStdString& strPath)
1141 if (NULL == m_pDB.get()) return -1;
1142 if (NULL == m_pDS.get()) return -1;
1144 CStdString strSQL=PrepareSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath='%s' and path.idPath=tvshowlinkpath.idPath",strPath.c_str());
1145 m_pDS->query(strSQL.c_str());
1146 if (m_pDS->num_rows() != 0)
1147 return m_pDS->fv("tvshowlinkpath.idShow").get_asInt();
1149 strSQL=PrepareSQL("insert into tvshow (idShow) values (NULL)");
1150 m_pDS->exec(strSQL.c_str());
1151 int idTvShow = (int)m_pDS->lastinsertid();
1153 // Get the creation datetime of the tvshow directory
1154 CDateTime dateAdded;
1155 // Skip looking at the files ctime/mtime if defined by the user through as.xml
1156 if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
1158 struct __stat64 buffer;
1159 if (XFILE::CFile::Stat(strPath, &buffer) == 0)
1161 time_t now = time(NULL);
1162 // Make sure we have a valid date (i.e. not in the future)
1163 if ((time_t)buffer.st_ctime <= now)
1165 struct tm *time = localtime((const time_t*)&buffer.st_ctime);
1172 if (!dateAdded.IsValid())
1173 dateAdded = CDateTime::GetCurrentDateTime();
1175 int idPath = AddPath(strPath, dateAdded.GetAsDBDateTime());
1176 strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
1177 m_pDS->exec(strSQL.c_str());
1183 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1188 //********************************************************************************************************************************
1189 int CVideoDatabase::AddEpisode(int idShow, const CStdString& strFilenameAndPath)
1193 if (NULL == m_pDB.get()) return -1;
1194 if (NULL == m_pDS.get()) return -1;
1196 int idFile = AddFile(strFilenameAndPath);
1199 UpdateFileDateAdded(idFile, strFilenameAndPath);
1201 CStdString strSQL=PrepareSQL("insert into episode (idEpisode, idFile, idShow) values (NULL, %i, %i)", idFile, idShow);
1202 m_pDS->exec(strSQL.c_str());
1203 return (int)m_pDS->lastinsertid();
1207 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1212 int CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
1216 if (NULL == m_pDB.get()) return -1;
1217 if (NULL == m_pDS.get()) return -1;
1219 int idMVideo = GetMusicVideoId(strFilenameAndPath);
1222 int idFile = AddFile(strFilenameAndPath);
1225 UpdateFileDateAdded(idFile, strFilenameAndPath);
1226 CStdString strSQL=PrepareSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %i)", idFile);
1227 m_pDS->exec(strSQL.c_str());
1228 idMVideo = (int)m_pDS->lastinsertid();
1235 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1240 //********************************************************************************************************************************
1241 int CVideoDatabase::AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value)
1245 if (NULL == m_pDB.get()) return -1;
1246 if (NULL == m_pDS.get()) return -1;
1248 CStdString strSQL = PrepareSQL("select %s from %s where %s like '%s'", firstField.c_str(), table.c_str(), secondField.c_str(), value.c_str());
1249 m_pDS->query(strSQL.c_str());
1250 if (m_pDS->num_rows() == 0)
1253 // doesnt exists, add it
1254 strSQL = PrepareSQL("insert into %s (%s, %s) values(NULL, '%s')", table.c_str(), firstField.c_str(), secondField.c_str(), value.c_str());
1255 m_pDS->exec(strSQL.c_str());
1256 int id = (int)m_pDS->lastinsertid();
1261 int id = m_pDS->fv(firstField).get_asInt();
1268 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, value.c_str() );
1274 int CVideoDatabase::AddSet(const CStdString& strSet)
1279 return AddToTable("sets", "idSet", "strSet", strSet);
1282 int CVideoDatabase::AddTag(const std::string& tag)
1287 return AddToTable("tag", "idTag", "strTag", tag);
1290 int CVideoDatabase::AddGenre(const CStdString& strGenre)
1292 return AddToTable("genre", "idGenre", "strGenre", strGenre);
1295 int CVideoDatabase::AddStudio(const CStdString& strStudio)
1297 return AddToTable("studio", "idStudio", "strStudio", strStudio);
1300 //********************************************************************************************************************************
1301 int CVideoDatabase::AddCountry(const CStdString& strCountry)
1303 return AddToTable("country", "idCountry", "strCountry", strCountry);
1306 int CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& thumbURLs, const CStdString &thumb)
1310 if (NULL == m_pDB.get()) return -1;
1311 if (NULL == m_pDS.get()) return -1;
1313 CStdString strSQL=PrepareSQL("select idActor from actors where strActor like '%s'", strActor.c_str());
1314 m_pDS->query(strSQL.c_str());
1315 if (m_pDS->num_rows() == 0)
1318 // doesnt exists, add it
1319 strSQL=PrepareSQL("insert into actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),thumbURLs.c_str());
1320 m_pDS->exec(strSQL.c_str());
1321 idActor = (int)m_pDS->lastinsertid();
1325 idActor = m_pDS->fv("idActor").get_asInt();
1327 // update the thumb url's
1328 if (!thumbURLs.empty())
1330 strSQL=PrepareSQL("update actors set strThumb='%s' where idActor=%i",thumbURLs.c_str(),idActor);
1331 m_pDS->exec(strSQL.c_str());
1336 SetArtForItem(idActor, "actor", "thumb", thumb);
1341 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
1348 void CVideoDatabase::AddLinkToActor(const char *table, int actorID, const char *field, int secondID, const CStdString &role, int order)
1352 if (NULL == m_pDB.get()) return ;
1353 if (NULL == m_pDS.get()) return ;
1355 CStdString strSQL=PrepareSQL("select * from actorlink%s where idActor=%i and id%s=%i", table, actorID, field, secondID);
1356 m_pDS->query(strSQL.c_str());
1357 if (m_pDS->num_rows() == 0)
1359 // doesnt exists, add it
1360 strSQL=PrepareSQL("insert into actorlink%s (idActor, id%s, strRole, iOrder) values(%i,%i,'%s',%i)", table, field, actorID, secondID, role.c_str(), order);
1361 m_pDS->exec(strSQL.c_str());
1367 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1371 void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1375 if (NULL == m_pDB.get()) return ;
1376 if (NULL == m_pDS.get()) return ;
1378 CStdString strSQL = PrepareSQL("select * from %s where %s=%i and %s=%i", table, firstField, firstID, secondField, secondID);
1379 if (typeField != NULL && type != NULL)
1380 strSQL += PrepareSQL(" and %s='%s'", typeField, type);
1381 m_pDS->query(strSQL.c_str());
1382 if (m_pDS->num_rows() == 0)
1384 // doesnt exists, add it
1385 if (typeField == NULL || type == NULL)
1386 strSQL = PrepareSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
1388 strSQL = PrepareSQL("insert into %s (%s,%s,%s) values(%i,%i,'%s')", table, firstField, secondField, typeField, firstID, secondID, type);
1389 m_pDS->exec(strSQL.c_str());
1395 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1399 void CVideoDatabase::RemoveFromLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1403 if (NULL == m_pDB.get()) return ;
1404 if (NULL == m_pDS.get()) return ;
1406 CStdString strSQL = PrepareSQL("DELETE FROM %s WHERE %s = %i AND %s = %i", table, firstField, firstID, secondField, secondID);
1407 if (typeField != NULL && type != NULL)
1408 strSQL += PrepareSQL(" AND %s='%s'", typeField, type);
1409 m_pDS->exec(strSQL.c_str());
1413 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1418 void CVideoDatabase::AddTagToItem(int idMovie, int idTag, const std::string &type)
1423 AddToLinkTable("taglinks", "idTag", idTag, "idMedia", idMovie, "media_type", type.c_str());
1426 void CVideoDatabase::RemoveTagFromItem(int idItem, int idTag, const std::string &type)
1431 RemoveFromLinkTable("taglinks", "idTag", idTag, "idMedia", idItem, "media_type", type.c_str());
1434 void CVideoDatabase::RemoveTagsFromItem(int idItem, const std::string &type)
1439 m_pDS2->exec(PrepareSQL("DELETE FROM taglinks WHERE idMedia=%d AND media_type='%s'", idItem, type.c_str()));
1443 void CVideoDatabase::AddCast(int idMedia, const char *table, const char *field, const std::vector< SActorInfo > &cast)
1448 int order = std::max_element(cast.begin(), cast.end())->order;
1449 for (CVideoInfoTag::iCast it = cast.begin(); it != cast.end(); ++it)
1451 int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
1452 AddLinkToActor(table, idActor, field, idMedia, it->strRole, it->order >= 0 ? it->order : ++order);
1456 void CVideoDatabase::AddArtistToMusicVideo(int idMVideo, int idArtist)
1458 AddToLinkTable("artistlinkmusicvideo", "idArtist", idArtist, "idMVideo", idMVideo);
1461 //****Directors + Writers****
1462 void CVideoDatabase::AddDirectorToMovie(int idMovie, int idDirector)
1464 AddToLinkTable("directorlinkmovie", "idDirector", idDirector, "idMovie", idMovie);
1467 void CVideoDatabase::AddDirectorToTvShow(int idTvShow, int idDirector)
1469 AddToLinkTable("directorlinktvshow", "idDirector", idDirector, "idShow", idTvShow);
1472 void CVideoDatabase::AddWriterToEpisode(int idEpisode, int idWriter)
1474 AddToLinkTable("writerlinkepisode", "idWriter", idWriter, "idEpisode", idEpisode);
1477 void CVideoDatabase::AddWriterToMovie(int idMovie, int idWriter)
1479 AddToLinkTable("writerlinkmovie", "idWriter", idWriter, "idMovie", idMovie);
1482 void CVideoDatabase::AddDirectorToEpisode(int idEpisode, int idDirector)
1484 AddToLinkTable("directorlinkepisode", "idDirector", idDirector, "idEpisode", idEpisode);
1487 void CVideoDatabase::AddDirectorToMusicVideo(int idMVideo, int idDirector)
1489 AddToLinkTable("directorlinkmusicvideo", "idDirector", idDirector, "idMVideo", idMVideo);
1493 void CVideoDatabase::AddStudioToMovie(int idMovie, int idStudio)
1495 AddToLinkTable("studiolinkmovie", "idStudio", idStudio, "idMovie", idMovie);
1498 void CVideoDatabase::AddStudioToTvShow(int idTvShow, int idStudio)
1500 AddToLinkTable("studiolinktvshow", "idStudio", idStudio, "idShow", idTvShow);
1503 void CVideoDatabase::AddStudioToMusicVideo(int idMVideo, int idStudio)
1505 AddToLinkTable("studiolinkmusicvideo", "idStudio", idStudio, "idMVideo", idMVideo);
1509 void CVideoDatabase::AddGenreToMovie(int idMovie, int idGenre)
1511 AddToLinkTable("genrelinkmovie", "idGenre", idGenre, "idMovie", idMovie);
1514 void CVideoDatabase::AddGenreToTvShow(int idTvShow, int idGenre)
1516 AddToLinkTable("genrelinktvshow", "idGenre", idGenre, "idShow", idTvShow);
1519 void CVideoDatabase::AddGenreToMusicVideo(int idMVideo, int idGenre)
1521 AddToLinkTable("genrelinkmusicvideo", "idGenre", idGenre, "idMVideo", idMVideo);
1525 void CVideoDatabase::AddCountryToMovie(int idMovie, int idCountry)
1527 AddToLinkTable("countrylinkmovie", "idCountry", idCountry, "idMovie", idMovie);
1530 //********************************************************************************************************************************
1531 bool CVideoDatabase::LoadVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details)
1533 if (GetMovieInfo(strFilenameAndPath, details))
1535 if (GetEpisodeInfo(strFilenameAndPath, details))
1537 if (GetMusicVideoInfo(strFilenameAndPath, details))
1539 if (GetFileInfo(strFilenameAndPath, details))
1545 bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
1549 if (NULL == m_pDB.get()) return false;
1550 if (NULL == m_pDS.get()) return false;
1551 int idMovie = GetMovieId(strFilenameAndPath);
1552 return (idMovie > 0); // index of zero is also invalid
1556 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1561 bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
1565 if (NULL == m_pDB.get()) return false;
1566 if (NULL == m_pDS.get()) return false;
1567 int idTvShow = GetTvShowId(strPath);
1568 return (idTvShow > 0); // index of zero is also invalid
1572 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1577 bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
1581 if (NULL == m_pDB.get()) return false;
1582 if (NULL == m_pDS.get()) return false;
1583 int idEpisode = GetEpisodeId(strFilenameAndPath);
1584 return (idEpisode > 0); // index of zero is also invalid
1588 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1593 bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
1597 if (NULL == m_pDB.get()) return false;
1598 if (NULL == m_pDS.get()) return false;
1599 int idMVideo = GetMusicVideoId(strFilenameAndPath);
1600 return (idMVideo > 0); // index of zero is also invalid
1604 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1609 void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath, int idTvShow /* = -1 */)
1613 if (NULL == m_pDB.get()) return ;
1614 if (NULL == m_pDS.get()) return ;
1618 idTvShow = GetTvShowId(strPath);
1624 strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
1625 m_pDS->exec(strSQL.c_str());
1627 strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
1628 m_pDS->exec(strSQL.c_str());
1630 strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
1631 m_pDS->exec(strSQL.c_str());
1633 strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
1634 m_pDS->exec(strSQL.c_str());
1636 // remove all info other than the id
1637 // we do this due to the way we have the link between the file + movie tables.
1639 std::vector<string> ids;
1640 for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1641 ids.push_back(StringUtils::Format("c%02d=NULL", iType));
1643 strSQL = "update tvshow set ";
1644 strSQL += StringUtils::Join(ids, ", ");
1645 strSQL += PrepareSQL(" where idShow=%i", idTvShow);
1646 m_pDS->exec(strSQL.c_str());
1650 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1654 //********************************************************************************************************************************
1655 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
1658 filter.join = "LEFT JOIN actorlinkmovie ON actorlinkmovie.idMovie=movieview.idMovie "
1659 "LEFT JOIN actors a ON a.idActor=actorlinkmovie.idActor "
1660 "LEFT JOIN directorlinkmovie ON directorlinkmovie.idMovie=movieview.idMovie "
1661 "LEFT JOIN actors d ON d.idActor=directorlinkmovie.idDirector";
1662 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1663 filter.group = "movieview.idMovie";
1664 GetMoviesByWhere("videodb://movies/titles/", filter, items);
1667 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
1670 filter.join = "LEFT JOIN actorlinktvshow ON actorlinktvshow.idShow=tvshowview.idShow "
1671 "LEFT JOIN actors a ON a.idActor=actorlinktvshow.idActor "
1672 "LEFT JOIN directorlinktvshow ON directorlinktvshow.idShow=tvshowview.idShow "
1673 "LEFT JOIN actors d ON d.idActor=directorlinktvshow.idDirector";
1674 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1675 filter.group = "tvshowview.idShow";
1676 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
1679 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
1682 filter.join = "LEFT JOIN actorlinkepisode ON actorlinkepisode.idEpisode=episodeview.idEpisode "
1683 "LEFT JOIN actors a ON a.idActor=actorlinkepisode.idActor "
1684 "LEFT JOIN directorlinkepisode ON directorlinkepisode.idEpisode=episodeview.idEpisode "
1685 "LEFT JOIN actors d ON d.idActor=directorlinkepisode.idDirector";
1686 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1687 filter.group = "episodeview.idEpisode";
1688 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
1691 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1696 if (NULL == m_pDB.get()) return ;
1697 if (NULL == m_pDS.get()) return ;
1700 if (strArtist.empty()) // TODO: SMARTPLAYLISTS what is this here for???
1701 strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1703 strSQL=PrepareSQL("select * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist where actors.strActor='%s'", strArtist.c_str());
1704 m_pDS->query( strSQL.c_str() );
1706 while (!m_pDS->eof())
1708 CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1709 CFileItemPtr pItem(new CFileItem(tag));
1710 pItem->SetLabel(StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator));
1718 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1722 //********************************************************************************************************************************
1723 bool CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
1727 // TODO: Optimize this - no need for all the queries!
1729 idMovie = GetMovieId(strFilenameAndPath);
1730 if (idMovie < 0) return false;
1732 CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
1733 if (!m_pDS->query(sql.c_str()))
1735 details = GetDetailsForMovie(m_pDS, true);
1736 return !details.IsEmpty();
1740 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1745 //********************************************************************************************************************************
1746 bool CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
1751 idTvShow = GetTvShowId(strPath);
1752 if (idTvShow < 0) return false;
1754 CStdString sql = PrepareSQL("SELECT * FROM tvshowview WHERE idShow=%i", idTvShow);
1755 if (!m_pDS->query(sql.c_str()))
1757 details = GetDetailsForTvShow(m_pDS, true);
1758 return !details.IsEmpty();
1762 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1767 bool CVideoDatabase::GetSeasonInfo(int idSeason, CVideoInfoTag& details)
1774 if (!m_pDB.get() || !m_pDS.get())
1777 CStdString sql = PrepareSQL("SELECT idShow FROM seasons WHERE idSeason=%i", idSeason);
1778 if (!m_pDS->query(sql.c_str()))
1782 if (m_pDS->num_rows() == 1)
1783 idShow = m_pDS->fv(0).get_asInt();
1790 CFileItemList seasons;
1791 if (!GetSeasonsNav(StringUtils::Format("videodb://tvshows/titles/%ld/", idShow), seasons, -1, -1, -1, -1, idShow, false) || seasons.Size() <= 0)
1794 for (int index = 0; index < seasons.Size(); index++)
1796 const CFileItemPtr season = seasons.Get(index);
1797 if (season->HasVideoInfoTag() && season->GetVideoInfoTag()->m_iDbId == idSeason && season->GetVideoInfoTag()->m_iIdShow == idShow)
1799 details = *season->GetVideoInfoTag();
1806 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
1811 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
1815 // TODO: Optimize this - no need for all the queries!
1817 idEpisode = GetEpisodeId(strFilenameAndPath);
1818 if (idEpisode < 0) return false;
1820 CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
1821 if (!m_pDS->query(sql.c_str()))
1823 details = GetDetailsForEpisode(m_pDS, true);
1824 return !details.IsEmpty();
1828 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1833 bool CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
1837 // TODO: Optimize this - no need for all the queries!
1839 idMVideo = GetMusicVideoId(strFilenameAndPath);
1840 if (idMVideo < 0) return false;
1842 CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
1843 if (!m_pDS->query(sql.c_str()))
1845 details = GetDetailsForMusicVideo(m_pDS, true);
1846 return !details.IsEmpty();
1850 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1855 bool CVideoDatabase::GetSetInfo(int idSet, CVideoInfoTag& details)
1863 filter.where = PrepareSQL("sets.idSet=%d", idSet);
1864 CFileItemList items;
1865 if (!GetSetsByWhere("videodb://movies/sets/", filter, items) ||
1866 items.Size() != 1 ||
1867 !items[0]->HasVideoInfoTag())
1870 details = *(items[0]->GetVideoInfoTag());
1871 return !details.IsEmpty();
1875 CLog::Log(LOGERROR, "%s (%d) failed", __FUNCTION__, idSet);
1880 bool CVideoDatabase::GetFileInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idFile /* = -1 */)
1885 idFile = GetFileId(strFilenameAndPath);
1889 CStdString sql = PrepareSQL("SELECT * FROM files "
1890 "JOIN path ON path.idPath = files.idPath "
1891 "LEFT JOIN bookmark ON bookmark.idFile = files.idFile AND bookmark.type = %i "
1892 "WHERE files.idFile = %i", CBookmark::RESUME, idFile);
1893 if (!m_pDS->query(sql.c_str()))
1896 details.m_iFileId = m_pDS->fv("files.idFile").get_asInt();
1897 details.m_strPath = m_pDS->fv("path.strPath").get_asString();
1898 CStdString strFileName = m_pDS->fv("files.strFilename").get_asString();
1899 ConstructPath(details.m_strFileNameAndPath, details.m_strPath, strFileName);
1900 details.m_playCount = max(details.m_playCount, m_pDS->fv("files.playCount").get_asInt());
1901 if (!details.m_lastPlayed.IsValid())
1902 details.m_lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
1903 if (!details.m_dateAdded.IsValid())
1904 details.m_dateAdded.SetFromDBDateTime(m_pDS->fv("files.dateAdded").get_asString());
1905 if (!details.m_resumePoint.IsSet())
1907 details.m_resumePoint.timeInSeconds = m_pDS->fv("bookmark.timeInSeconds").get_asInt();
1908 details.m_resumePoint.totalTimeInSeconds = m_pDS->fv("bookmark.totalTimeInSeconds").get_asInt();
1909 details.m_resumePoint.type = CBookmark::RESUME;
1912 return !details.IsEmpty();
1916 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1921 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1923 // add all directors
1924 for (unsigned int i = 0; i < details.m_director.size(); i++)
1925 vecDirectors.push_back(AddActor(details.m_director[i],""));
1928 for (unsigned int i = 0; i < details.m_genre.size(); i++)
1929 vecGenres.push_back(AddGenre(details.m_genre[i]));
1931 for (unsigned int i = 0; i < details.m_studio.size(); i++)
1932 vecStudios.push_back(AddStudio(details.m_studio[i]));
1935 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1937 std::vector<std::string> conditions;
1938 for (int i = min + 1; i < max; ++i)
1940 switch (offsets[i].type)
1942 case VIDEODB_TYPE_STRING:
1943 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str()));
1945 case VIDEODB_TYPE_INT:
1946 conditions.push_back(PrepareSQL("c%02d='%i'", i, *(int*)(((char*)&details)+offsets[i].offset)));
1948 case VIDEODB_TYPE_COUNT:
1950 int value = *(int*)(((char*)&details)+offsets[i].offset);
1952 conditions.push_back(PrepareSQL("c%02d=%i", i, value));
1954 conditions.push_back(PrepareSQL("c%02d=NULL", i));
1957 case VIDEODB_TYPE_BOOL:
1958 conditions.push_back(PrepareSQL("c%02d='%s'", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false"));
1960 case VIDEODB_TYPE_FLOAT:
1961 conditions.push_back(PrepareSQL("c%02d='%f'", i, *(float*)(((char*)&details)+offsets[i].offset)));
1963 case VIDEODB_TYPE_STRINGARRAY:
1964 conditions.push_back(PrepareSQL("c%02d='%s'", i, StringUtils::Join(*((std::vector<std::string>*)(((char*)&details)+offsets[i].offset)),
1965 g_advancedSettings.m_videoItemSeparator).c_str()));
1967 case VIDEODB_TYPE_DATE:
1968 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDate().c_str()));
1970 case VIDEODB_TYPE_DATETIME:
1971 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDateTime().c_str()));
1975 return StringUtils::Join(conditions, ",");
1978 //********************************************************************************************************************************
1979 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMovie /* = -1 */)
1986 idMovie = GetMovieId(strFilenameAndPath);
1989 DeleteMovie(strFilenameAndPath, true, idMovie); // true to keep the table entry
1992 // only add a new movie if we don't already have a valid idMovie
1993 // (DeleteMovie is called with bKeepId == true so the movie won't
1994 // be removed from the movie table)
1995 idMovie = AddMovie(strFilenameAndPath);
1998 RollbackTransaction();
2003 vector<int> vecDirectors;
2004 vector<int> vecGenres;
2005 vector<int> vecStudios;
2006 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2008 for (unsigned int i = 0; i < vecGenres.size(); ++i)
2009 AddGenreToMovie(idMovie, vecGenres[i]);
2011 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2012 AddDirectorToMovie(idMovie, vecDirectors[i]);
2014 for (unsigned int i = 0; i < vecStudios.size(); ++i)
2015 AddStudioToMovie(idMovie, vecStudios[i]);
2018 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2019 AddWriterToMovie(idMovie, AddActor(details.m_writingCredits[i],""));
2021 AddCast(idMovie, "movie", "movie", details.m_cast);
2025 if (!details.m_strSet.empty())
2027 idSet = AddSet(details.m_strSet);
2028 // add art if not available
2029 map<string, string> setArt;
2030 if (!GetArtForItem(idSet, "set", setArt))
2031 SetArtForItem(idSet, "set", artwork);
2035 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2037 int idTag = AddTag(details.m_tags[i]);
2038 AddTagToItem(idMovie, idTag, "movie");
2042 for (unsigned int i = 0; i < details.m_country.size(); i++)
2043 AddCountryToMovie(idMovie, AddCountry(details.m_country[i]));
2045 if (details.HasStreamDetails())
2046 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2048 SetArtForItem(idMovie, "movie", artwork);
2050 // query DB for any movies matching imdbid and year
2051 CStdString strSQL = PrepareSQL("select files.playCount, files.lastPlayed from movie,files where files.idFile=movie.idFile and movie.c%02d='%s' and movie.c%02d=%i and movie.idMovie!=%i and files.playCount > 0", VIDEODB_ID_IDENT, details.m_strIMDBNumber.c_str(), VIDEODB_ID_YEAR, details.m_iYear, idMovie);
2052 m_pDS->query(strSQL.c_str());
2056 int playCount = m_pDS->fv("files.playCount").get_asInt();
2058 CDateTime lastPlayed;
2059 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2061 int idFile = GetFileId(strFilenameAndPath);
2063 // update with playCount and lastPlayed
2064 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2065 m_pDS->exec(strSQL.c_str());
2070 // update our movie table (we know it was added already above)
2071 // and insert the new row
2072 CStdString sql = "update movie set " + GetValueString(details, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
2074 sql += PrepareSQL(", idSet = %i", idSet);
2076 sql += ", idSet = NULL";
2077 sql += PrepareSQL(" where idMovie=%i", idMovie);
2078 m_pDS->exec(sql.c_str());
2079 CommitTransaction();
2085 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2087 RollbackTransaction();
2091 int CVideoDatabase::SetDetailsForMovieSet(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idSet /* = -1 */)
2093 if (details.m_strTitle.empty())
2101 idSet = AddSet(details.m_strTitle);
2104 RollbackTransaction();
2109 SetArtForItem(idSet, "set", artwork);
2111 // and insert the new row
2112 CStdString sql = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", details.m_strTitle.c_str(), idSet);
2113 m_pDS->exec(sql.c_str());
2114 CommitTransaction();
2120 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
2122 RollbackTransaction();
2126 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, map<string, string> > &seasonArt, int idTvShow /*= -1 */)
2130 if (!m_pDB.get() || !m_pDS.get())
2132 CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
2139 idTvShow = GetTvShowId(strPath);
2142 DeleteDetailsForTvShow(strPath, idTvShow);
2145 idTvShow = AddTvShow(strPath);
2148 RollbackTransaction();
2153 vector<int> vecDirectors;
2154 vector<int> vecGenres;
2155 vector<int> vecStudios;
2156 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2158 AddCast(idTvShow, "tvshow", "show", details.m_cast);
2161 for (i = 0; i < vecGenres.size(); ++i)
2162 AddGenreToTvShow(idTvShow, vecGenres[i]);
2164 for (i = 0; i < vecDirectors.size(); ++i)
2165 AddDirectorToTvShow(idTvShow, vecDirectors[i]);
2167 for (i = 0; i < vecStudios.size(); ++i)
2168 AddStudioToTvShow(idTvShow, vecStudios[i]);
2171 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2173 int idTag = AddTag(details.m_tags[i]);
2174 AddTagToItem(idTvShow, idTag, "tvshow");
2177 // add "all seasons" - the rest are added in SetDetailsForEpisode
2178 AddSeason(idTvShow, -1);
2180 SetArtForItem(idTvShow, "tvshow", artwork);
2181 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
2183 int idSeason = AddSeason(idTvShow, i->first);
2185 SetArtForItem(idSeason, "season", i->second);
2188 // and insert the new row
2189 CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
2190 sql += PrepareSQL(" where idShow=%i", idTvShow);
2191 m_pDS->exec(sql.c_str());
2193 CommitTransaction();
2199 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2201 RollbackTransaction();
2205 int CVideoDatabase::SetDetailsForSeason(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idShow, int idSeason /* = -1 */)
2207 if (idShow < 0 || details.m_iSeason < 0)
2215 idSeason = AddSeason(idShow, details.m_iSeason);
2218 RollbackTransaction();
2223 SetArtForItem(idSeason, "season", artwork);
2225 // and insert the new row
2226 CStdString sql = PrepareSQL("UPDATE seasons SET season=%i WHERE idSeason=%i", details.m_iSeason, idSeason);
2227 m_pDS->exec(sql.c_str());
2228 CommitTransaction();
2234 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
2236 RollbackTransaction();
2240 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idShow, int idEpisode)
2246 idEpisode = GetEpisodeId(strFilenameAndPath);
2249 DeleteEpisode(strFilenameAndPath, idEpisode, true); // true to keep the table entry
2252 // only add a new episode if we don't already have a valid idEpisode
2253 // (DeleteEpisode is called with bKeepId == true so the episode won't
2254 // be removed from the episode table)
2255 idEpisode = AddEpisode(idShow,strFilenameAndPath);
2258 RollbackTransaction();
2263 vector<int> vecDirectors;
2264 vector<int> vecGenres;
2265 vector<int> vecStudios;
2266 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2268 AddCast(idEpisode, "episode", "episode", details.m_cast);
2271 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2272 AddWriterToEpisode(idEpisode, AddActor(details.m_writingCredits[i],""));
2274 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2276 AddDirectorToEpisode(idEpisode, vecDirectors[i]);
2279 if (details.HasStreamDetails())
2281 if (details.m_iFileId != -1)
2282 SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2284 SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
2287 // ensure we have this season already added
2288 AddSeason(idShow, details.m_iSeason);
2290 SetArtForItem(idEpisode, "episode", artwork);
2292 // query DB for any episodes matching idShow, Season and Episode
2293 CStdString strSQL = PrepareSQL("select files.playCount, files.lastPlayed from episode, files where files.idFile=episode.idFile and episode.c%02d=%i and episode.c%02d=%i AND episode.idShow=%i and episode.idEpisode!=%i and files.playCount > 0",VIDEODB_ID_EPISODE_SEASON, details.m_iSeason, VIDEODB_ID_EPISODE_EPISODE, details.m_iEpisode, idShow, idEpisode);
2294 m_pDS->query(strSQL.c_str());
2298 int playCount = m_pDS->fv("files.playCount").get_asInt();
2300 CDateTime lastPlayed;
2301 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2303 int idFile = GetFileId(strFilenameAndPath);
2305 // update with playCount and lastPlayed
2306 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2307 m_pDS->exec(strSQL.c_str());
2312 // and insert the new row
2313 CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
2314 sql += PrepareSQL(" where idEpisode=%i", idEpisode);
2315 m_pDS->exec(sql.c_str());
2316 CommitTransaction();
2322 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2324 RollbackTransaction();
2328 int CVideoDatabase::GetSeasonId(int showID, int season)
2330 CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
2331 CStdString id = GetSingleValue("seasons", "idSeason", sql);
2334 return strtol(id.c_str(), NULL, 10);
2337 int CVideoDatabase::AddSeason(int showID, int season)
2339 int seasonId = GetSeasonId(showID, season);
2342 if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
2343 seasonId = (int)m_pDS->lastinsertid();
2348 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
2355 idMVideo = GetMusicVideoId(strFilenameAndPath);
2358 DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
2361 // only add a new musicvideo if we don't already have a valid idMVideo
2362 // (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
2363 // be removed from the musicvideo table)
2364 idMVideo = AddMusicVideo(strFilenameAndPath);
2367 RollbackTransaction();
2372 vector<int> vecDirectors;
2373 vector<int> vecGenres;
2374 vector<int> vecStudios;
2375 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2378 if (!details.m_artist.empty())
2380 for (unsigned int i = 0; i < details.m_artist.size(); i++)
2382 CStdString artist = details.m_artist[i];
2383 StringUtils::Trim(artist);
2384 int idArtist = AddActor(artist,"");
2385 AddArtistToMusicVideo(idMVideo, idArtist);
2390 for (i = 0; i < vecGenres.size(); ++i)
2392 AddGenreToMusicVideo(idMVideo, vecGenres[i]);
2395 for (i = 0; i < vecDirectors.size(); ++i)
2397 AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
2400 for (i = 0; i < vecStudios.size(); ++i)
2402 AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2406 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2408 int idTag = AddTag(details.m_tags[i]);
2409 AddTagToItem(idMVideo, idTag, "musicvideo");
2412 if (details.HasStreamDetails())
2413 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2415 SetArtForItem(idMVideo, "musicvideo", artwork);
2417 // update our movie table (we know it was added already above)
2418 // and insert the new row
2419 CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
2420 sql += PrepareSQL(" where idMVideo=%i", idMVideo);
2421 m_pDS->exec(sql.c_str());
2422 CommitTransaction();
2428 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2430 RollbackTransaction();
2434 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2436 // AddFile checks to make sure the file isn't already in the DB first
2437 int idFile = AddFile(strFileNameAndPath);
2440 SetStreamDetailsForFileId(details, idFile);
2443 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2451 m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2453 for (int i=1; i<=details.GetVideoStreamCount(); i++)
2455 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2456 "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration, strStereoMode) "
2457 "VALUES (%i,%i,'%s',%f,%i,%i,%i,'%s')",
2458 idFile, (int)CStreamDetail::VIDEO,
2459 details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2460 details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i),
2461 details.GetStereoMode(i).c_str()));
2463 for (int i=1; i<=details.GetAudioStreamCount(); i++)
2465 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2466 "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2467 "VALUES (%i,%i,'%s',%i,'%s')",
2468 idFile, (int)CStreamDetail::AUDIO,
2469 details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2470 details.GetAudioLanguage(i).c_str()));
2472 for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2474 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2475 "(idFile, iStreamType, strSubtitleLanguage) "
2476 "VALUES (%i,%i,'%s')",
2477 idFile, (int)CStreamDetail::SUBTITLE,
2478 details.GetSubtitleLanguage(i).c_str()));
2481 // update the runtime information, if empty
2482 if (details.GetVideoDuration())
2484 vector< pair<string, int> > tables;
2485 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
2486 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
2487 tables.push_back(make_pair("musicvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
2488 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
2490 CStdString sql = PrepareSQL("update %s set c%02d=%d where idFile=%d and c%02d=''",
2491 i->first.c_str(), i->second, details.GetVideoDuration(), idFile, i->second);
2496 CommitTransaction();
2500 RollbackTransaction();
2501 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2505 //********************************************************************************************************************************
2506 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2510 if (NULL == m_pDB.get()) return ;
2511 if (NULL == m_pDS.get()) return ;
2513 if (idMovie < 0) return ;
2516 if (iType == VIDEODB_CONTENT_MOVIES)
2517 strSQL=PrepareSQL("select path.strPath,files.strFileName from path, files, movie where path.idPath=files.idPath and files.idFile=movie.idFile and movie.idMovie=%i order by strFilename", idMovie );
2518 if (iType == VIDEODB_CONTENT_EPISODES)
2519 strSQL=PrepareSQL("select path.strPath,files.strFileName from path, files, episode where path.idPath=files.idPath and files.idFile=episode.idFile and episode.idEpisode=%i order by strFilename", idMovie );
2520 if (iType == VIDEODB_CONTENT_TVSHOWS)
2521 strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2522 if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2523 strSQL=PrepareSQL("select path.strPath,files.strFileName from path, files, musicvideo where path.idPath=files.idPath and files.idFile=musicvideo.idFile and musicvideo.idMVideo=%i order by strFilename", idMovie );
2525 m_pDS->query( strSQL.c_str() );
2528 if (iType != VIDEODB_CONTENT_TVSHOWS)
2530 CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2531 ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2534 filePath = m_pDS->fv("path.strPath").get_asString();
2540 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2544 //********************************************************************************************************************************
2545 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend, long partNumber)
2549 if (URIUtils::IsStack(strFilenameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(strFilenameAndPath),false).IsDVDImage())
2551 CStackDirectory dir;
2552 CFileItemList fileList;
2553 dir.GetDirectory(strFilenameAndPath, fileList);
2556 for (int i = fileList.Size() - 1; i >= 0; i--) // put the bookmarks of the highest part first in the list
2557 GetBookMarksForFile(fileList[i]->GetPath(), bookmarks, type, true, (i+1));
2561 int idFile = GetFileId(strFilenameAndPath);
2562 if (idFile < 0) return ;
2564 bookmarks.erase(bookmarks.begin(), bookmarks.end());
2565 if (NULL == m_pDB.get()) return ;
2566 if (NULL == m_pDS.get()) return ;
2568 CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2569 m_pDS->query( strSQL.c_str() );
2570 while (!m_pDS->eof())
2573 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2574 bookmark.partNumber = partNumber;
2575 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2576 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2577 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2578 bookmark.player = m_pDS->fv("player").get_asString();
2579 bookmark.type = type;
2580 if (type == CBookmark::EPISODE)
2582 CStdString strSQL2=PrepareSQL("select c%02d, c%02d from episode where c%02d=%i order by c%02d, c%02d", VIDEODB_ID_EPISODE_EPISODE, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_BOOKMARK, m_pDS->fv("idBookmark").get_asInt(), VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTEPISODE);
2583 m_pDS2->query(strSQL2.c_str());
2584 bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2585 bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2588 bookmarks.push_back(bookmark);
2591 //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2597 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2601 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2603 VECBOOKMARKS bookmarks;
2604 GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2605 if (bookmarks.size() > 0)
2607 bookmark = bookmarks[0];
2613 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2615 if (!m_pDB.get() || !m_pDS.get())
2618 int fileID = GetFileId(strFilenameAndPath);
2624 CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2625 m_pDS->exec(sql.c_str());
2629 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2633 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2637 CStdString strSQL = PrepareSQL("select * from episodeview where idFile=%i order by c%02d, c%02d asc", GetFileId(strFilenameAndPath), VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTEPISODE);
2638 m_pDS->query(strSQL.c_str());
2639 while (!m_pDS->eof())
2641 episodes.push_back(GetDetailsForEpisode(m_pDS));
2648 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2652 //********************************************************************************************************************************
2653 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2657 int idFile = AddFile(strFilenameAndPath);
2660 if (NULL == m_pDB.get()) return ;
2661 if (NULL == m_pDS.get()) return ;
2665 if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2667 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2669 else if (type == CBookmark::STANDARD) // get the same bookmark again, and update. not sure here as a dvd can have same time in multiple places, state will differ thou
2671 /* get a bookmark within the same time as previous */
2672 double mintime = bookmark.timeInSeconds - 0.5f;
2673 double maxtime = bookmark.timeInSeconds + 0.5f;
2674 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=%i and (timeInSeconds between %f and %f) and playerState='%s'", idFile, (int)type, mintime, maxtime, bookmark.playerState.c_str());
2677 if (type != CBookmark::EPISODE)
2680 m_pDS->query( strSQL.c_str() );
2681 if (m_pDS->num_rows() != 0)
2682 idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2685 // update or insert depending if it existed before
2686 if (idBookmark >= 0 )
2687 strSQL=PrepareSQL("update bookmark set timeInSeconds = %f, totalTimeInSeconds = %f, thumbNailImage = '%s', player = '%s', playerState = '%s' where idBookmark = %i", bookmark.timeInSeconds, bookmark.totalTimeInSeconds, bookmark.thumbNailImage.c_str(), bookmark.player.c_str(), bookmark.playerState.c_str(), idBookmark);
2689 strSQL=PrepareSQL("insert into bookmark (idBookmark, idFile, timeInSeconds, totalTimeInSeconds, thumbNailImage, player, playerState, type) values(NULL,%i,%f,%f,'%s','%s','%s', %i)", idFile, bookmark.timeInSeconds, bookmark.totalTimeInSeconds, bookmark.thumbNailImage.c_str(), bookmark.player.c_str(), bookmark.playerState.c_str(), (int)type);
2691 m_pDS->exec(strSQL.c_str());
2695 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2699 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2703 int idFile = GetFileId(strFilenameAndPath);
2704 if (idFile < 0) return ;
2705 if (NULL == m_pDB.get()) return ;
2706 if (NULL == m_pDS.get()) return ;
2708 /* a litle bit uggly, we clear first bookmark that is within one second of given */
2709 /* should be no problem since we never add bookmarks that are closer than that */
2710 double mintime = bookmark.timeInSeconds - 0.5f;
2711 double maxtime = bookmark.timeInSeconds + 0.5f;
2712 CStdString strSQL = PrepareSQL("select idBookmark from bookmark where idFile=%i and type=%i and playerState like '%s' and player like '%s' and (timeInSeconds between %f and %f)", idFile, type, bookmark.playerState.c_str(), bookmark.player.c_str(), mintime, maxtime);
2714 m_pDS->query( strSQL.c_str() );
2715 if (m_pDS->num_rows() != 0)
2717 int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2718 strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2719 m_pDS->exec(strSQL.c_str());
2720 if (type == CBookmark::EPISODE)
2722 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i and c%02d=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile, VIDEODB_ID_EPISODE_BOOKMARK, idBookmark);
2723 m_pDS->exec(strSQL.c_str());
2731 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2735 //********************************************************************************************************************************
2736 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2740 int idFile = GetFileId(strFilenameAndPath);
2741 if (idFile < 0) return ;
2742 if (NULL == m_pDB.get()) return ;
2743 if (NULL == m_pDS.get()) return ;
2745 CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2746 m_pDS->exec(strSQL.c_str());
2747 if (type == CBookmark::EPISODE)
2749 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2750 m_pDS->exec(strSQL.c_str());
2755 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2760 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2764 CStdString strSQL = PrepareSQL("select bookmark.* from bookmark join episode on episode.c%02d=bookmark.idBookmark where episode.idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2765 m_pDS->query( strSQL.c_str() );
2768 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2769 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2770 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2771 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2772 bookmark.player = m_pDS->fv("player").get_asString();
2773 bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2784 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2790 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2794 int idFile = GetFileId(tag.m_strFileNameAndPath);
2795 // delete the current episode for the selected episode number
2796 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where c%02d=%i and c%02d=%i and idFile=%i)", VIDEODB_ID_EPISODE_BOOKMARK, VIDEODB_ID_EPISODE_SEASON, tag.m_iSeason, VIDEODB_ID_EPISODE_EPISODE, tag.m_iEpisode, idFile);
2797 m_pDS->exec(strSQL.c_str());
2799 AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2800 int idBookmark = (int)m_pDS->lastinsertid();
2801 strSQL = PrepareSQL("update episode set c%02d=%i where c%02d=%i and c%02d=%i and idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idBookmark, VIDEODB_ID_EPISODE_SEASON, tag.m_iSeason, VIDEODB_ID_EPISODE_EPISODE, tag.m_iEpisode, idFile);
2802 m_pDS->exec(strSQL.c_str());
2806 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2810 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2814 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2815 m_pDS->exec(strSQL.c_str());
2816 strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2817 m_pDS->exec(strSQL.c_str());
2821 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2825 //********************************************************************************************************************************
2826 void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
2832 GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
2834 DeleteMovie(path, bKeepId, idMovie);
2837 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
2841 if (NULL == m_pDB.get()) return ;
2842 if (NULL == m_pDS.get()) return ;
2845 idMovie = GetMovieId(strFilenameAndPath);
2853 strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2854 m_pDS->exec(strSQL.c_str());
2856 strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2857 m_pDS->exec(strSQL.c_str());
2859 strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2860 m_pDS->exec(strSQL.c_str());
2862 strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2863 m_pDS->exec(strSQL.c_str());
2865 strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2866 m_pDS->exec(strSQL.c_str());
2868 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2870 // keep the movie table entry, linking to tv shows, and bookmarks
2871 // so we can update the data in place
2872 // the ancilliary tables are still purged
2875 ClearBookMarksOfFile(strFilenameAndPath);
2877 strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2878 m_pDS->exec(strSQL.c_str());
2880 strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2881 m_pDS->exec(strSQL.c_str());
2883 CStdString strPath, strFileName;
2884 SplitPath(strFilenameAndPath,strPath,strFileName);
2885 InvalidatePathHash(strPath);
2888 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2890 AnnounceRemove("movie", idMovie);
2892 CommitTransaction();
2897 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2898 RollbackTransaction();
2902 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2908 GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2910 DeleteTvShow(path, bKeepId, idTvShow);
2913 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2917 if (NULL == m_pDB.get()) return ;
2918 if (NULL == m_pDS.get()) return ;
2921 idTvShow = GetTvShowId(strPath);
2928 CStdString strSQL=PrepareSQL("select episode.idEpisode,path.strPath,files.strFileName from episode,path,files where episode.idShow=%i and episode.idFile=files.idFile and files.idPath=path.idPath",idTvShow);
2929 m_pDS2->query(strSQL.c_str());
2930 while (!m_pDS2->eof())
2932 CStdString strFilenameAndPath;
2933 CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2934 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2935 ConstructPath(strFilenameAndPath, strPath, strFileName);
2936 DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2940 DeleteDetailsForTvShow(strPath, idTvShow);
2942 strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2943 m_pDS->exec(strSQL.c_str());
2945 // keep tvshow table and movielink table so we can update data in place
2948 strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2949 m_pDS->exec(strSQL.c_str());
2951 strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2952 m_pDS->exec(strSQL.c_str());
2954 strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2955 m_pDS->exec(strSQL.c_str());
2957 InvalidatePathHash(strPath);
2960 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2962 AnnounceRemove("tvshow", idTvShow);
2964 CommitTransaction();
2969 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2970 RollbackTransaction();
2974 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2980 GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2982 DeleteEpisode(path, idEpisode, bKeepId);
2985 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
2989 if (NULL == m_pDB.get()) return ;
2990 if (NULL == m_pDS.get()) return ;
2993 idEpisode = GetEpisodeId(strFilenameAndPath);
3000 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3002 AnnounceRemove("episode", idEpisode);
3005 strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
3006 m_pDS->exec(strSQL.c_str());
3008 strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
3009 m_pDS->exec(strSQL.c_str());
3011 strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
3012 m_pDS->exec(strSQL.c_str());
3014 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3016 // keep episode table entry and bookmarks so we can update the data in place
3017 // the ancilliary tables are still purged
3020 ClearBookMarksOfFile(strFilenameAndPath);
3022 strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
3023 m_pDS->exec(strSQL.c_str());
3029 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3033 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
3035 if (idMusicVideo < 0)
3039 GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
3041 DeleteMusicVideo(path, bKeepId, idMusicVideo);
3044 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
3048 if (NULL == m_pDB.get()) return ;
3049 if (NULL == m_pDS.get()) return ;
3052 idMVideo = GetMusicVideoId(strFilenameAndPath);
3060 strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
3061 m_pDS->exec(strSQL.c_str());
3063 strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
3064 m_pDS->exec(strSQL.c_str());
3066 strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
3067 m_pDS->exec(strSQL.c_str());
3069 strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
3070 m_pDS->exec(strSQL.c_str());
3072 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3074 // keep the music video table entry and bookmarks so we can update data in place
3075 // the ancilliary tables are still purged
3078 ClearBookMarksOfFile(strFilenameAndPath);
3080 strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
3081 m_pDS->exec(strSQL.c_str());
3083 CStdString strPath, strFileName;
3084 SplitPath(strFilenameAndPath,strPath,strFileName);
3085 InvalidatePathHash(strPath);
3088 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3090 AnnounceRemove("musicvideo", idMVideo);
3092 CommitTransaction();
3097 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3098 RollbackTransaction();
3102 void CVideoDatabase::DeleteStreamDetails(int idFile)
3104 m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
3107 void CVideoDatabase::DeleteSet(int idSet)
3111 if (NULL == m_pDB.get()) return ;
3112 if (NULL == m_pDS.get()) return ;
3115 strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
3116 m_pDS->exec(strSQL.c_str());
3117 strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3118 m_pDS->exec(strSQL.c_str());
3122 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3126 void CVideoDatabase::ClearMovieSet(int idMovie)
3128 SetMovieSet(idMovie, -1);
3131 void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
3134 ExecuteQuery(PrepareSQL("update movie set idSet = %i where idMovie = %i", idSet, idMovie));
3136 ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
3139 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3143 if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3147 if (mediaType == VIDEODB_CONTENT_MOVIES)
3149 else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3151 else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3152 type = "musicvideo";
3157 strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3158 m_pDS->exec(strSQL.c_str());
3162 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3166 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3168 GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3171 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3173 for (int i = min + 1; i < max; i++)
3175 switch (offsets[i].type)
3177 case VIDEODB_TYPE_STRING:
3178 *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3180 case VIDEODB_TYPE_INT:
3181 case VIDEODB_TYPE_COUNT:
3182 *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3184 case VIDEODB_TYPE_BOOL:
3185 *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3187 case VIDEODB_TYPE_FLOAT:
3188 *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3190 case VIDEODB_TYPE_STRINGARRAY:
3191 *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3193 case VIDEODB_TYPE_DATE:
3194 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3196 case VIDEODB_TYPE_DATETIME:
3197 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3203 DWORD movieTime = 0;
3206 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3208 CVideoInfoTag details;
3213 case VIDEODB_CONTENT_MOVIES:
3214 GetMovieInfo("", details, id);
3216 case VIDEODB_CONTENT_TVSHOWS:
3217 GetTvShowInfo("", details, id);
3219 case VIDEODB_CONTENT_EPISODES:
3220 GetEpisodeInfo("", details, id);
3222 case VIDEODB_CONTENT_MUSICVIDEOS:
3223 GetMusicVideoInfo("", details, id);
3232 bool CVideoDatabase::GetStreamDetails(CFileItem& item)
3234 // Note that this function (possibly) creates VideoInfoTags for items that don't have one yet!
3237 if (item.HasVideoInfoTag())
3238 fileId = item.GetVideoInfoTag()->m_iFileId;
3241 fileId = GetFileId(item);
3246 // Have a file id, get stream details if available (creates tag either way)
3247 item.GetVideoInfoTag()->m_iFileId = fileId;
3248 return GetStreamDetails(*item.GetVideoInfoTag());
3251 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3253 if (tag.m_iFileId < 0)
3256 bool retVal = false;
3258 CStreamDetails& details = tag.m_streamDetails;
3261 auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3264 CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3269 CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3272 case CStreamDetail::VIDEO:
3274 CStreamDetailVideo *p = new CStreamDetailVideo();
3275 p->m_strCodec = pDS->fv(2).get_asString();
3276 p->m_fAspect = pDS->fv(3).get_asFloat();
3277 p->m_iWidth = pDS->fv(4).get_asInt();
3278 p->m_iHeight = pDS->fv(5).get_asInt();
3279 p->m_iDuration = pDS->fv(10).get_asInt();
3280 p->m_strStereoMode = pDS->fv(11).get_asString();
3281 details.AddStream(p);
3285 case CStreamDetail::AUDIO:
3287 CStreamDetailAudio *p = new CStreamDetailAudio();
3288 p->m_strCodec = pDS->fv(6).get_asString();
3289 if (pDS->fv(7).get_isNull())
3290 p->m_iChannels = -1;
3292 p->m_iChannels = pDS->fv(7).get_asInt();
3293 p->m_strLanguage = pDS->fv(8).get_asString();
3294 details.AddStream(p);
3298 case CStreamDetail::SUBTITLE:
3300 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3301 p->m_strLanguage = pDS->fv(9).get_asString();
3302 details.AddStream(p);
3315 CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3317 details.DetermineBestStreams();
3319 if (details.GetVideoDuration() > 0)
3320 tag.m_duration = details.GetVideoDuration();
3325 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3327 if (tag.m_iFileId < 0)
3334 if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3336 CStackDirectory dir;
3337 CFileItemList fileList;
3338 dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3339 tag.m_resumePoint.Reset();
3340 for (int i = fileList.Size() - 1; i >= 0; i--)
3343 if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3345 tag.m_resumePoint = bookmark;
3346 tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3354 CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3355 m_pDS2->query( strSQL.c_str() );
3358 tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3359 tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3360 tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3361 tag.m_resumePoint.type = CBookmark::RESUME;
3369 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3375 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3377 return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3380 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3382 CVideoInfoTag details;
3387 DWORD time = XbmcThreads::SystemClockMillis();
3388 int idMovie = record->at(0).get_asInt();
3390 GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3392 details.m_iDbId = idMovie;
3393 details.m_type = "movie";
3395 details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3396 details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3397 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3398 details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3399 CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3400 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3401 details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3402 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3403 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3404 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3405 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3406 details.m_resumePoint.type = CBookmark::RESUME;
3408 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3412 GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3414 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3415 details.m_strPictureURL.Parse();
3418 CStdString strSQL = PrepareSQL("SELECT tag.strTag FROM tag, taglinks WHERE taglinks.idMedia = %i AND taglinks.media_type = 'movie' AND taglinks.idTag = tag.idTag ORDER BY tag.idTag", idMovie);
3419 m_pDS2->query(strSQL.c_str());
3420 while (!m_pDS2->eof())
3422 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3426 // create tvshowlink string
3428 GetLinksToTvShow(idMovie,links);
3429 for (unsigned int i=0;i<links.size();++i)
3431 CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3432 VIDEODB_ID_TV_TITLE,links[i]);
3433 m_pDS2->query(strSQL.c_str());
3435 details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3439 // get streamdetails
3440 GetStreamDetails(details);
3445 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3447 return GetDetailsForTvShow(pDS->get_sql_record(), getDetails, item);
3450 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3452 CVideoInfoTag details;
3457 DWORD time = XbmcThreads::SystemClockMillis();
3458 int idTvShow = record->at(0).get_asInt();
3460 GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3461 details.m_iDbId = idTvShow;
3462 details.m_type = "tvshow";
3463 details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3464 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3465 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3466 details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3467 details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3468 details.m_strShowPath = details.m_strPath;
3469 details.m_strShowTitle = details.m_strTitle;
3470 if (details.m_premiered.IsValid())
3471 details.m_iYear = details.m_premiered.GetYear();
3473 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3477 GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3480 CStdString strSQL = PrepareSQL("SELECT tag.strTag FROM tag, taglinks WHERE taglinks.idMedia = %i AND taglinks.media_type = 'tvshow' AND taglinks.idTag = tag.idTag ORDER BY tag.idTag", idTvShow);
3481 m_pDS2->query(strSQL.c_str());
3482 while (!m_pDS2->eof())
3484 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3488 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3489 details.m_strPictureURL.Parse();
3494 item->m_dateTime = details.m_premiered;
3495 item->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
3496 item->SetProperty("totalepisodes", details.m_iEpisode);
3497 item->SetProperty("numepisodes", details.m_iEpisode); // will be changed later to reflect watchmode setting
3498 item->SetProperty("watchedepisodes", details.m_playCount);
3499 item->SetProperty("unwatchedepisodes", details.m_iEpisode - details.m_playCount);
3501 details.m_playCount = (details.m_iEpisode <= details.m_playCount) ? 1 : 0;
3506 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3508 return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3511 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3513 CVideoInfoTag details;
3518 DWORD time = XbmcThreads::SystemClockMillis();
3519 int idEpisode = record->at(0).get_asInt();
3521 GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3522 details.m_iDbId = idEpisode;
3523 details.m_type = "episode";
3524 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3525 details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3526 CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3527 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3528 details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3529 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3530 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3531 details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3532 details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3533 details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3534 details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3535 details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3536 details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3537 details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3539 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3540 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3541 details.m_resumePoint.type = CBookmark::RESUME;
3543 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3547 GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3548 GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3550 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3551 details.m_strPictureURL.Parse();
3552 CStdString strSQL = PrepareSQL("select * from bookmark join episode on episode.c%02d=bookmark.idBookmark where episode.idEpisode=%i and bookmark.type=%i", VIDEODB_ID_EPISODE_BOOKMARK,details.m_iDbId,CBookmark::EPISODE);
3553 m_pDS2->query(strSQL.c_str());
3555 details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3558 // get streamdetails
3559 GetStreamDetails(details);
3564 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3566 return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3569 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3571 CVideoInfoTag details;
3573 unsigned int time = XbmcThreads::SystemClockMillis();
3574 int idMVideo = record->at(0).get_asInt();
3576 GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3577 details.m_iDbId = idMVideo;
3578 details.m_type = "musicvideo";
3580 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3581 details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3582 CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3583 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3584 details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3585 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3586 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3587 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3588 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3589 details.m_resumePoint.type = CBookmark::RESUME;
3591 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3596 CStdString strSQL = PrepareSQL("SELECT tag.strTag FROM tag, taglinks WHERE taglinks.idMedia = %i AND taglinks.media_type = 'musicvideo' AND taglinks.idTag = tag.idTag ORDER BY tag.idTag", idMVideo);
3597 m_pDS2->query(strSQL.c_str());
3598 while (!m_pDS2->eof())
3600 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3605 details.m_strPictureURL.Parse();
3607 // get streamdetails
3608 GetStreamDetails(details);
3613 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3617 if (!m_pDB.get()) return;
3618 if (!m_pDS2.get()) return;
3620 CStdString sql = PrepareSQL("SELECT actors.strActor,"
3621 " actorlink%s.strRole,"
3622 " actorlink%s.iOrder,"
3627 " actorlink%s.idActor=actors.idActor"
3629 " art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3630 "WHERE actorlink%s.%s=%i "
3631 "ORDER BY actorlink%s.iOrder",table.c_str(), table.c_str(), table.c_str(), table.c_str(), table.c_str(), table_id.c_str(), type_id, table.c_str());
3632 m_pDS2->query(sql.c_str());
3633 while (!m_pDS2->eof())
3636 info.strName = m_pDS2->fv(0).get_asString();
3638 for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3640 if (i->strName == info.strName)
3648 info.strRole = m_pDS2->fv(1).get_asString();
3649 info.order = m_pDS2->fv(2).get_asInt();
3650 info.thumbUrl.ParseString(m_pDS2->fv(3).get_asString());
3651 info.thumb = m_pDS2->fv(4).get_asString();
3652 cast.push_back(info);
3660 CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3664 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3665 /// \retval Returns true if the settings exist, false otherwise.
3666 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3670 // obtain the FileID (if it exists)
3671 #ifdef NEW_VIDEODB_METHODS
3672 if (NULL == m_pDB.get()) return false;
3673 if (NULL == m_pDS.get()) return false;
3674 CStdString strPath, strFileName;
3675 URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3676 CStdString strSQL=PrepareSQL("select * from settings, files, path where settings.idFile=files.idFile and path.idPath=files.idPath and path.strPath='%s' and files.strFileName='%s'", strPath.c_str() , strFileName.c_str());
3678 int idFile = GetFileId(strFilenameAndPath);
3679 if (idFile < 0) return false;
3680 if (NULL == m_pDB.get()) return false;
3681 if (NULL == m_pDS.get()) return false;
3682 // ok, now obtain the settings for this file
3683 CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3685 m_pDS->query( strSQL.c_str() );
3686 if (m_pDS->num_rows() > 0)
3687 { // get the video settings info
3688 settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3689 settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3690 settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3691 settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3692 settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3693 settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3694 settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3695 settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3696 settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3697 settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3698 settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3699 settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3700 settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3701 settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3702 settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3703 settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3704 settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3705 settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3706 settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3707 settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3708 settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3709 settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3710 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3711 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3712 settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3713 settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3714 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3715 settings.m_StereoMode = m_pDS->fv("StereoMode").get_asInt();
3716 settings.m_StereoInvert = m_pDS->fv("StereoInvert").get_asBool();
3717 settings.m_SubtitleCached = false;
3725 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3730 /// \brief Sets the settings for a particular video file
3731 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3735 if (NULL == m_pDB.get()) return ;
3736 if (NULL == m_pDS.get()) return ;
3737 int idFile = AddFile(strFilenameAndPath);
3740 CStdString strSQL = StringUtils::Format("select * from settings where idFile=%i", idFile);
3741 m_pDS->query( strSQL.c_str() );
3742 if (m_pDS->num_rows() > 0)
3746 strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3747 "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3748 "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3749 "DeinterlaceMode=%i,",
3750 setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3751 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3752 setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3753 setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3754 setting.m_DeinterlaceMode);
3756 strSQL2=PrepareSQL("ResumeTime=%i,Crop=%i,CropLeft=%i,CropRight=%i,CropTop=%i,CropBottom=%i,StereoMode=%i,StereoInvert=%i where idFile=%i\n", setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom, setting.m_StereoMode, setting.m_StereoInvert, idFile);
3758 m_pDS->exec(strSQL.c_str());
3764 strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3765 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3766 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3767 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3768 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode,StereoMode,StereoInvert) "
3770 strSQL += PrepareSQL("(%i,%i,%i,%f,%f,%f,%i,%i,%f,%i,%f,%f,%f,%f,%f,%i,%i,%i,%i,%i,%i,%i,%f,%f,%i,%i,%i,%i,%i,%i)",
3771 idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3772 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3773 setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3774 setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3775 setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3776 setting.m_DeinterlaceMode, setting.m_StereoMode, setting.m_StereoInvert);
3777 m_pDS->exec(strSQL.c_str());
3782 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3786 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3788 for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3789 SetArtForItem(mediaId, mediaType, i->first, i->second);
3792 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3796 if (NULL == m_pDB.get()) return;
3797 if (NULL == m_pDS.get()) return;
3799 // don't set <foo>.<bar> art types - these are derivative types from parent items
3800 if (artType.find('.') != string::npos)
3803 CStdString sql = PrepareSQL("SELECT art_id FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str());
3804 m_pDS->query(sql.c_str());
3807 int artId = m_pDS->fv(0).get_asInt();
3809 sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3810 m_pDS->exec(sql.c_str());
3815 sql = PrepareSQL("INSERT INTO art(media_id, media_type, type, url) VALUES (%d, '%s', '%s', '%s')", mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3816 m_pDS->exec(sql.c_str());
3821 CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3825 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3829 if (NULL == m_pDB.get()) return false;
3830 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3832 CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3833 m_pDS2->query(sql.c_str());
3834 while (!m_pDS2->eof())
3836 art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3840 return !art.empty();
3844 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3849 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3851 std::string query = PrepareSQL("SELECT url FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str());
3852 return GetSingleValue(query, m_pDS2);
3855 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::string &artType)
3857 return ExecuteQuery(PrepareSQL("DELETE FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str()));
3860 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::set<std::string> &artTypes)
3863 for (set<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
3864 result &= RemoveArtForItem(mediaId, mediaType, *i);
3869 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3873 if (NULL == m_pDB.get()) return false;
3874 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3876 // get all seasons for this show
3877 CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3878 m_pDS2->query(sql.c_str());
3880 vector< pair<int, int> > seasons;
3881 while (!m_pDS2->eof())
3883 seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3888 for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3890 map<string, string> art;
3891 GetArtForItem(i->first, "season", art);
3892 seasonArt.insert(make_pair(i->second,art));
3898 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3903 bool CVideoDatabase::GetArtTypes(const std::string &mediaType, std::vector<std::string> &artTypes)
3907 if (NULL == m_pDB.get()) return false;
3908 if (NULL == m_pDS.get()) return false;
3910 CStdString sql = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str());
3911 int numRows = RunQuery(sql);
3913 return numRows == 0;
3915 while (!m_pDS->eof())
3917 artTypes.push_back(m_pDS->fv(0).get_asString());
3925 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str());
3930 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3931 /// \retval Returns true if the stack times exist, false otherwise.
3932 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> ×)
3936 // obtain the FileID (if it exists)
3937 int idFile = GetFileId(filePath);
3938 if (idFile < 0) return false;
3939 if (NULL == m_pDB.get()) return false;
3940 if (NULL == m_pDS.get()) return false;
3941 // ok, now obtain the settings for this file
3942 CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3943 m_pDS->query( strSQL.c_str() );
3944 if (m_pDS->num_rows() > 0)
3945 { // get the video settings info
3946 CStdStringArray timeString;
3948 StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3950 for (unsigned int i = 0; i < timeString.size(); i++)
3952 times.push_back(atoi(timeString[i].c_str()));
3953 timeTotal += atoi(timeString[i].c_str());
3956 return (timeTotal > 0);
3962 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3967 /// \brief Sets the stack times for a particular video file
3968 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> ×)
3972 if (NULL == m_pDB.get()) return ;
3973 if (NULL == m_pDS.get()) return ;
3974 int idFile = AddFile(filePath);
3978 // delete any existing items
3979 m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3982 CStdString timeString = StringUtils::Format("%i", times[0]);
3983 for (unsigned int i = 1; i < times.size(); i++)
3984 timeString += StringUtils::Format(",%i", times[i]);
3986 m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3990 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3994 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
3996 if(URIUtils::IsMultiPath(strPath))
3998 vector<CStdString> paths;
3999 CMultiPathDirectory::GetPaths(strPath, paths);
4001 for(unsigned i=0;i<paths.size();i++)
4002 RemoveContentForPath(paths[i], progress);
4007 if (NULL == m_pDB.get()) return ;
4008 if (NULL == m_pDS.get()) return ;
4012 progress->SetHeading(700);
4013 progress->SetLine(0, "");
4014 progress->SetLine(1, 313);
4015 progress->SetLine(2, 330);
4016 progress->SetPercentage(0);
4017 progress->StartModal();
4018 progress->ShowProgressBar(true);
4020 vector< pair<int,string> > paths;
4021 GetSubPaths(strPath, paths);
4023 for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
4025 bool bMvidsChecked=false;
4028 progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
4029 progress->Progress();
4032 if (HasTvShowInfo(i->second))
4033 DeleteTvShow(i->second);
4036 CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
4037 m_pDS2->query(strSQL.c_str());
4040 strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4041 m_pDS2->query(strSQL.c_str());
4042 bMvidsChecked = true;
4044 while (!m_pDS2->eof())
4046 CStdString strMoviePath;
4047 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
4048 ConstructPath(strMoviePath, i->second, strFileName);
4049 if (HasMovieInfo(strMoviePath))
4050 DeleteMovie(strMoviePath);
4051 if (HasMusicVideoInfo(strMoviePath))
4052 DeleteMusicVideo(strMoviePath);
4054 if (m_pDS2->eof() && !bMvidsChecked)
4056 strSQL =PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4057 m_pDS2->query(strSQL.c_str());
4058 bMvidsChecked = true;
4062 m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
4068 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
4074 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
4076 // if we have a multipath, set scraper for all contained paths too
4077 if(URIUtils::IsMultiPath(filePath))
4079 vector<CStdString> paths;
4080 CMultiPathDirectory::GetPaths(filePath, paths);
4082 for(unsigned i=0;i<paths.size();i++)
4083 SetScraperForPath(paths[i],scraper,settings);
4088 if (NULL == m_pDB.get()) return ;
4089 if (NULL == m_pDS.get()) return ;
4091 int idPath = AddPath(filePath);
4097 if (settings.exclude)
4098 { //NB See note in ::GetScraperForPath about strContent=='none'
4099 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
4102 { // catch clearing content, but not excluding
4103 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
4107 CStdString content = TranslateContent(scraper->Content());
4108 strSQL=PrepareSQL("update path set strContent='%s', strScraper='%s', scanRecursive=%i, useFolderNames=%i, strSettings='%s', noUpdate=%i, exclude=0 where idPath=%i", content.c_str(), scraper->ID().c_str(),settings.recurse,settings.parent_name,scraper->GetPathSettings().c_str(),settings.noupdate, idPath);
4110 m_pDS->exec(strSQL.c_str());
4114 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4118 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
4122 if (NULL == m_pDB.get()) return false;
4123 if (NULL == m_pDS.get()) return false;
4125 CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
4126 if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
4128 bool found = m_pDS->fv(0).get_asInt() > 0;
4134 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
4142 CArtItem() { art_id = 0; media_id = 0; };
4150 void CVideoDatabase::UpdateTables(int iVersion)
4154 m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4155 m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4158 { // add seasons table
4159 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4160 // insert all seasons for each show
4161 m_pDS->query("SELECT idShow FROM tvshow");
4162 while (!m_pDS->eof())
4164 CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4169 " JOIN tvshowlinkepisode ON"
4170 " episode.idEpisode=tvshowlinkepisode.idEpisode"
4171 " WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4172 m_pDS2->exec(sql.c_str());
4173 // and the "all seasons node"
4174 sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4175 m_pDS2->exec(sql.c_str());
4181 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4183 CMediaSettings::Get().SetVideoNeedsUpdate(63);
4184 CSettings::Get().Save();
4187 { // add idShow to episode table
4188 m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4189 m_pDS->query("SELECT idEpisode FROM episode");
4190 while (!m_pDS->eof())
4192 int idEpisode = m_pDS->fv(0).get_asInt();
4193 CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4194 m_pDS2->exec(update.c_str());
4197 m_pDS->exec("DROP TABLE tvshowlinkepisode");
4201 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4202 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4205 { // add idSet to movie table
4206 m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4207 m_pDS->query("SELECT idMovie FROM movie");
4208 while (!m_pDS->eof())
4210 int idMovie = m_pDS->fv(0).get_asInt();
4211 CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4212 m_pDS2->exec(sql.c_str());
4215 m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4218 { // update old art URLs
4219 m_pDS->query("select art_id,url from art where url like 'image://%%'");
4220 vector< pair<int, string> > art;
4221 while (!m_pDS->eof())
4223 art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4227 for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4228 m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4231 { // update URL encoded paths
4232 m_pDS->query("select idFile, strFilename from files");
4233 vector< pair<int, string> > files;
4234 while (!m_pDS->eof())
4236 files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4241 for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4243 std::string filename = i->second;
4244 bool update = URIUtils::UpdateUrlEncoding(filename) &&
4245 (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4249 m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4253 { // Update thumb to poster or banner as applicable
4254 CTextureDatabase db;
4257 m_pDS->query("select art_id,url,media_id,media_type from art where type='thumb' and media_type in ('movie', 'musicvideo', 'tvshow', 'season', 'set')");
4258 vector<CArtItem> art;
4259 while (!m_pDS->eof())
4261 CTextureDetails details;
4262 if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4265 item.art_id = m_pDS->fv(0).get_asInt();
4266 item.art_url = m_pDS->fv(1).get_asString();
4267 item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4268 item.media_id = m_pDS->fv(2).get_asInt();
4269 item.media_type = m_pDS->fv(3).get_asString();
4270 if (item.art_type != "thumb")
4271 art.push_back(item);
4276 for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4278 if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4279 m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4281 m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4286 { // update the runtime columns
4287 vector< pair<string, int> > tables;
4288 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4289 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4290 tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4291 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4293 CStdString sql = PrepareSQL("select id%s,c%02d from %s where c%02d != ''", i->first.c_str(), i->second, (i->first=="mvideo")?"musicvideo":i->first.c_str(), i->second);
4294 m_pDS->query(sql.c_str());
4295 vector< pair<int, int> > videos;
4296 while (!m_pDS->eof())
4298 int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4300 videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4304 for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4305 m_pDS->exec(PrepareSQL("update %s set c%02d=%d where id%s=%d", (i->first=="mvideo")?"musicvideo":i->first.c_str(), i->second, j->second, i->first.c_str(), j->first));
4310 m_pDS->exec("ALTER TABLE settings ADD StereoMode integer");
4311 m_pDS->exec("ALTER TABLE settings ADD StereoInvert bool");
4314 m_pDS->exec("ALTER TABLE streamdetails ADD strStereoMode text");
4317 int CVideoDatabase::GetSchemaVersion() const
4322 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4324 SScanSettings settings;
4325 bool foundDirectly = false;
4326 ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4327 if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4328 return false; // episodes
4329 return settings.parent_name_root; // shows, movies, musicvids
4332 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4334 if(URIUtils::IsMultiPath(strPath))
4336 vector<CStdString> paths;
4337 CMultiPathDirectory::GetPaths(strPath, paths);
4340 for(unsigned i=0;i<paths.size();i++)
4341 ret |= GetPlayCounts(paths[i], items);
4346 if (URIUtils::IsPlugin(strPath))
4349 pathID = GetPathId(url.GetWithoutFilename());
4352 pathID = GetPathId(strPath);
4354 return false; // path (and thus files) aren't in the database
4359 if (NULL == m_pDB.get()) return false;
4360 if (NULL == m_pDS.get()) return false;
4362 // TODO: also test a single query for the above and below
4363 CStdString sql = PrepareSQL(
4365 " files.strFilename, files.playCount,"
4366 " bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4368 " LEFT JOIN bookmark ON"
4369 " files.idFile = bookmark.idFile AND bookmark.type = %i"
4370 " WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4372 if (RunQuery(sql) <= 0)
4375 items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4376 while (!m_pDS->eof())
4379 ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4380 CFileItemPtr item = items.Get(path);
4383 item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4384 if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4386 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4387 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4388 item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4397 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4402 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4404 int id = GetFileId(item);
4406 return 0; // not in db, so not watched
4411 if (NULL == m_pDB.get()) return -1;
4412 if (NULL == m_pDS.get()) return -1;
4414 CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4416 if (m_pDS->query(strSQL.c_str()))
4418 // there should only ever be one row returned
4419 if (m_pDS->num_rows() == 1)
4420 count = m_pDS->fv(0).get_asInt();
4427 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4432 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4434 if (NULL == m_pDB.get()) return;
4435 if (NULL == m_pDS.get()) return;
4436 if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4439 if (type == VIDEODB_CONTENT_TVSHOWS)
4440 exec = PrepareSQL("UPDATE tvshow set c%02d='%s' WHERE idShow=%i", VIDEODB_ID_TV_FANART, item.GetVideoInfoTag()->m_fanart.m_xml.c_str(), item.GetVideoInfoTag()->m_iDbId);
4441 else if (type == VIDEODB_CONTENT_MOVIES)
4442 exec = PrepareSQL("UPDATE movie set c%02d='%s' WHERE idMovie=%i", VIDEODB_ID_FANART, item.GetVideoInfoTag()->m_fanart.m_xml.c_str(), item.GetVideoInfoTag()->m_iDbId);
4446 m_pDS->exec(exec.c_str());
4448 if (type == VIDEODB_CONTENT_TVSHOWS)
4449 AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4450 else if (type == VIDEODB_CONTENT_MOVIES)
4451 AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4455 CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4459 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4462 if (item.HasProperty("original_listitem_url") &&
4463 URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4465 CFileItem item2(item);
4466 item2.SetPath(item.GetProperty("original_listitem_url").asString());
4467 id = AddFile(item2);
4474 // and mark as watched
4477 if (NULL == m_pDB.get()) return ;
4478 if (NULL == m_pDS.get()) return ;
4483 if (!date.IsValid())
4484 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4486 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4490 if (!date.IsValid())
4491 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4493 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4496 m_pDS->exec(strSQL.c_str());
4498 // We only need to announce changes to video items in the library
4499 if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4501 // Only provide the "playcount" value if it has actually changed
4502 if (item.GetVideoInfoTag()->m_playCount != count)
4505 data["playcount"] = count;
4506 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4509 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4514 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4518 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4520 SetPlayCount(item, GetPlayCount(item) + 1);
4523 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4525 SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4528 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4532 if (NULL == m_pDB.get()) return ;
4533 if (NULL == m_pDS.get()) return ;
4535 if (iType == VIDEODB_CONTENT_MOVIES)
4537 CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4540 else if (iType == VIDEODB_CONTENT_EPISODES)
4542 CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4543 content = "episode";
4545 else if (iType == VIDEODB_CONTENT_TVSHOWS)
4547 CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4550 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4552 CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4553 content = "musicvideo";
4555 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4557 CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4558 CStdString strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4559 m_pDS->exec(strSQL.c_str());
4562 if (!content.empty())
4564 SetSingleValue(iType, idMovie, FieldTitle, strNewMovieTitle);
4565 AnnounceUpdate(content, idMovie);
4570 CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4574 bool CVideoDatabase::UpdateVideoSortTitle(int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType /* = VIDEODB_CONTENT_MOVIES */)
4578 if (NULL == m_pDB.get() || NULL == m_pDS.get())
4580 if (iType != VIDEODB_CONTENT_MOVIES && iType != VIDEODB_CONTENT_TVSHOWS)
4583 CStdString content = "movie";
4584 if (iType == VIDEODB_CONTENT_TVSHOWS)
4587 if (SetSingleValue(iType, idDb, FieldSortTitle, strNewSortTitle))
4589 AnnounceUpdate(content, idDb);
4595 CLog::Log(LOGERROR, "%s (int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType) failed on ID: %i and Sort Title: %s", __FUNCTION__, idDb, strNewSortTitle.c_str());
4601 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4602 void CVideoDatabase::EraseVideoSettings()
4606 CLog::Log(LOGINFO, "Deleting settings information for all movies");
4607 m_pDS->exec("delete from settings");
4611 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4615 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4617 return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4620 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4622 return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4625 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4627 return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4630 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4634 if (NULL == m_pDB.get()) return false;
4635 if (NULL == m_pDS.get()) return false;
4638 Filter extFilter = filter;
4639 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4641 if (idContent == VIDEODB_CONTENT_MOVIES)
4643 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4644 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath, files.playCount", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4645 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on %s.id%s = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile join path on path.idPath = files.idPath",
4646 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4648 else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4650 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4651 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4652 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join episodeview on %slinktvshow.idShow = episodeview.idShow join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
4653 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4655 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4657 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4658 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath, files.playCount", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4659 extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on %s.id%s = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath",
4660 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4667 if (idContent == VIDEODB_CONTENT_MOVIES)
4669 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4670 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, count(1), count(files.playCount)", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4671 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on %s.id%s = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile",
4672 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4673 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4675 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4677 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4678 extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4679 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4680 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4682 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4684 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4685 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, count(1), count(files.playCount)", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4686 extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on %s.id%s = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile",
4687 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4688 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4696 extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4697 extFilter.group.clear();
4698 extFilter.order.clear();
4700 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4702 CVideoDbUrl videoUrl;
4703 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4706 int iRowsFound = RunQuery(strSQL);
4707 if (iRowsFound <= 0)
4708 return iRowsFound == 0;
4712 CFileItemPtr pItem(new CFileItem());
4713 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4720 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4722 map<int, pair<CStdString,int> > mapItems;
4723 map<int, pair<CStdString,int> >::iterator it;
4724 while (!m_pDS->eof())
4726 int id = m_pDS->fv(0).get_asInt();
4727 CStdString str = m_pDS->fv(1).get_asString();
4729 // was this already found?
4730 it = mapItems.find(id);
4731 if (it == mapItems.end())
4734 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
4736 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4737 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
4738 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4739 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
4746 for (it = mapItems.begin(); it != mapItems.end(); ++it)
4748 CFileItemPtr pItem(new CFileItem(it->second.first));
4749 pItem->GetVideoInfoTag()->m_iDbId = it->first;
4750 pItem->GetVideoInfoTag()->m_type = type;
4752 CVideoDbUrl itemUrl = videoUrl;
4753 CStdString path = StringUtils::Format("%ld/", it->first);
4754 itemUrl.AppendPath(path);
4755 pItem->SetPath(itemUrl.ToString());
4757 pItem->m_bIsFolder = true;
4758 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4759 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
4760 if (!items.Contains(pItem->GetPath()))
4762 pItem->SetLabelPreformated(true);
4769 while (!m_pDS->eof())
4771 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4772 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
4773 pItem->GetVideoInfoTag()->m_type = type;
4775 CVideoDbUrl itemUrl = videoUrl;
4776 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
4777 itemUrl.AppendPath(path);
4778 pItem->SetPath(itemUrl.ToString());
4780 pItem->m_bIsFolder = true;
4781 pItem->SetLabelPreformated(true);
4782 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4783 { // fv(3) is the number of videos watched, fv(2) is the total number. We set the playcount
4784 // only if the number of videos watched is equal to the total number (i.e. every video watched)
4785 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
4796 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4801 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4803 CStdString mediaType;
4804 if (idContent == VIDEODB_CONTENT_MOVIES)
4805 mediaType = "movie";
4806 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4807 mediaType = "tvshow";
4808 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4809 mediaType = "musicvideo";
4815 if (NULL == m_pDB.get()) return false;
4816 if (NULL == m_pDS.get()) return false;
4818 CStdString strSQL = "SELECT %s FROM taglinks ";
4820 Filter extFilter = filter;
4821 extFilter.fields = "tag.idTag, tag.strTag";
4822 extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
4824 if (idContent == (int)VIDEODB_CONTENT_MOVIES)
4825 extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
4827 extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
4828 extFilter.AppendGroup("taglinks.idTag");
4832 extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
4833 extFilter.group.clear();
4834 extFilter.order.clear();
4836 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4838 // parse the base path to get additional filters
4839 CVideoDbUrl videoUrl;
4840 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4843 int iRowsFound = RunQuery(strSQL);
4844 if (iRowsFound <= 0)
4845 return iRowsFound == 0;
4849 CFileItemPtr pItem(new CFileItem());
4850 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4857 while (!m_pDS->eof())
4859 int idTag = m_pDS->fv(0).get_asInt();
4861 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4862 pItem->m_bIsFolder = true;
4863 pItem->GetVideoInfoTag()->m_iDbId = idTag;
4864 pItem->GetVideoInfoTag()->m_type = "tag";
4866 CVideoDbUrl itemUrl = videoUrl;
4867 CStdString path = StringUtils::Format("%ld/", idTag);
4868 itemUrl.AppendPath(path);
4869 pItem->SetPath(itemUrl.ToString());
4871 if (!items.Contains(pItem->GetPath()))
4873 pItem->SetLabelPreformated(true);
4885 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4890 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool ignoreSingleMovieSets /* = false */)
4892 if (idContent != VIDEODB_CONTENT_MOVIES)
4895 return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
4898 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
4902 if (NULL == m_pDB.get()) return false;
4903 if (NULL == m_pDS.get()) return false;
4905 CVideoDbUrl videoUrl;
4906 if (!videoUrl.FromString(strBaseDir))
4909 Filter setFilter = filter;
4910 setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
4911 if (!setFilter.order.empty())
4912 setFilter.order += ",";
4913 setFilter.order += "sets.idSet";
4915 if (!GetMoviesByWhere(strBaseDir, setFilter, items))
4919 if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
4929 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4934 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4938 if (NULL == m_pDB.get()) return false;
4939 if (NULL == m_pDS.get()) return false;
4941 CVideoDbUrl videoUrl;
4942 if (!videoUrl.FromString(strBaseDir))
4945 CStdString strSQL = "select %s from musicvideoview ";
4946 Filter extFilter = filter;
4947 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
4948 extFilter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
4949 extFilter.AppendJoin(PrepareSQL("join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
4950 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4952 extFilter.fields += " path.strPath";
4953 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath");
4957 videoUrl.AddOption("artistid", idArtist);
4959 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
4963 extFilter.fields = "COUNT(1)";
4964 extFilter.group.clear();
4965 extFilter.order.clear();
4967 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4969 if (!BuildSQL(videoUrl.ToString(), strSQL, extFilter, strSQL, videoUrl))
4972 int iRowsFound = RunQuery(strSQL);
4973 if (iRowsFound <= 0)
4974 return iRowsFound == 0;
4978 CFileItemPtr pItem(new CFileItem());
4979 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4986 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4988 map<int, pair<CStdString,CStdString> > mapAlbums;
4989 map<int, pair<CStdString,CStdString> >::iterator it;
4990 while (!m_pDS->eof())
4992 int lidMVideo = m_pDS->fv(1).get_asInt();
4993 CStdString strAlbum = m_pDS->fv(0).get_asString();
4994 it = mapAlbums.find(lidMVideo);
4995 // was this genre already found?
4996 if (it == mapAlbums.end())
4999 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5000 mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5006 for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5008 if (!it->second.first.empty())
5010 CFileItemPtr pItem(new CFileItem(it->second.first));
5012 CVideoDbUrl itemUrl = videoUrl;
5013 CStdString path = StringUtils::Format("%ld/", it->first);
5014 itemUrl.AppendPath(path);
5015 pItem->SetPath(itemUrl.ToString());
5017 pItem->m_bIsFolder=true;
5018 pItem->SetLabelPreformated(true);
5019 if (!items.Contains(pItem->GetPath()))
5021 pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5029 while (!m_pDS->eof())
5031 if (!m_pDS->fv(0).get_asString().empty())
5033 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5035 CVideoDbUrl itemUrl = videoUrl;
5036 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(1).get_asInt());
5037 itemUrl.AppendPath(path);
5038 pItem->SetPath(itemUrl.ToString());
5040 pItem->m_bIsFolder=true;
5041 pItem->SetLabelPreformated(true);
5042 if (!items.Contains(pItem->GetPath()))
5044 pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5053 // CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5058 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5063 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5065 return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5068 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5070 return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5073 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5075 if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5076 { // set thumbs - ideally this should be in the normal thumb setting routines
5077 for (int i = 0; i < items.Size() && !countOnly; i++)
5079 CFileItemPtr pItem = items[i];
5080 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5081 pItem->SetIconImage("DefaultArtist.png");
5083 pItem->SetIconImage("DefaultActor.png");
5090 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5092 if (NULL == m_pDB.get()) return false;
5093 if (NULL == m_pDS.get()) return false;
5097 // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5098 // point. This means that we *MUST* filter these levels as you'll get double ups. Ideally we'd allow playcount
5099 // to filter through as we normally do for tvshows to save this happening.
5100 // Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5101 // Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5104 // General routine that the other actor/director/writer routines call
5106 // get primary genres for movies
5108 Filter extFilter = filter;
5109 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5111 if (idContent == VIDEODB_CONTENT_MOVIES)
5113 strSQL = "select %s from actors ";
5114 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5115 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on actors.idActor = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile join path on path.idPath = files.idPath",
5116 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5118 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5120 strSQL = "select %s from actors ";
5121 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5122 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on actors.idActor = %slinktvshow.id%s join episodeview on %slinktvshow.idShow = episodeview.idShow join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
5123 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5125 else if (idContent == VIDEODB_CONTENT_EPISODES)
5127 strSQL = "select %s from actors ";
5128 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5129 extFilter.AppendJoin(PrepareSQL("join %slinkepisode on actors.idActor = %slinkepisode.id%s join episodeview on %slinkepisode.idEpisode = episodeview.idEpisode join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
5130 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5132 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5134 strSQL = "select %s from actors ";
5135 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5136 extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on actors.idActor = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath",
5137 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5144 if (idContent == VIDEODB_CONTENT_MOVIES)
5146 strSQL ="select %s from actors ";
5147 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5148 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on actors.idActor = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile",
5149 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5150 extFilter.AppendGroup("actors.idActor");
5152 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5154 strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5155 extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5156 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5157 type.c_str(), type.c_str(), type.c_str()));
5159 else if (idContent == VIDEODB_CONTENT_EPISODES)
5161 strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5162 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5163 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5164 type.c_str(), type.c_str(), type.c_str()));
5165 extFilter.AppendGroup("actors.idActor");
5167 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5169 strSQL = "select %s from actors ";
5170 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5171 extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on actors.idActor = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile",
5172 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5173 extFilter.AppendGroup("actors.idActor");
5181 extFilter.fields = "COUNT(1)";
5182 extFilter.group.clear();
5183 extFilter.order.clear();
5185 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5187 CVideoDbUrl videoUrl;
5188 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5192 unsigned int time = XbmcThreads::SystemClockMillis();
5193 if (!m_pDS->query(strSQL.c_str())) return false;
5194 CLog::Log(LOGDEBUG, "%s - query took %i ms",
5195 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5196 int iRowsFound = m_pDS->num_rows();
5197 if (iRowsFound == 0)
5205 CFileItemPtr pItem(new CFileItem());
5206 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5213 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5215 map<int, CActor> mapActors;
5216 map<int, CActor>::iterator it;
5218 while (!m_pDS->eof())
5220 int idActor = m_pDS->fv(0).get_asInt();
5222 actor.name = m_pDS->fv(1).get_asString();
5223 actor.thumb = m_pDS->fv(2).get_asString();
5224 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5225 actor.playcount = m_pDS->fv(3).get_asInt();
5226 it = mapActors.find(idActor);
5227 // is this actor already known?
5228 if (it == mapActors.end())
5231 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5232 mapActors.insert(pair<int, CActor>(idActor, actor));
5238 for (it=mapActors.begin();it != mapActors.end();++it)
5240 CFileItemPtr pItem(new CFileItem(it->second.name));
5242 CVideoDbUrl itemUrl = videoUrl;
5243 CStdString path = StringUtils::Format("%ld/", it->first);
5244 itemUrl.AppendPath(path);
5245 pItem->SetPath(itemUrl.ToString());
5247 pItem->m_bIsFolder=true;
5248 pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5249 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5250 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5251 pItem->GetVideoInfoTag()->m_type = type;
5257 while (!m_pDS->eof())
5261 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5263 CVideoDbUrl itemUrl = videoUrl;
5264 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5265 itemUrl.AppendPath(path);
5266 pItem->SetPath(itemUrl.ToString());
5268 pItem->m_bIsFolder=true;
5269 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5270 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5271 pItem->GetVideoInfoTag()->m_type = type;
5272 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5274 // fv(4) is the number of videos watched, fv(3) is the total number. We set the playcount
5275 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5276 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5278 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5279 pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5286 CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5287 return items.Size() > 0;
5292 CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5293 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5300 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5305 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5309 if (NULL == m_pDB.get()) return false;
5310 if (NULL == m_pDS.get()) return false;
5313 Filter extFilter = filter;
5314 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5316 if (idContent == VIDEODB_CONTENT_MOVIES)
5318 strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5319 extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5321 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5323 strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5324 extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5326 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5328 strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5329 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5337 if (idContent == VIDEODB_CONTENT_MOVIES)
5339 strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5340 extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5341 extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5343 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5344 strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5345 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5347 strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5348 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5349 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5355 CVideoDbUrl videoUrl;
5356 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5359 int iRowsFound = RunQuery(strSQL);
5360 if (iRowsFound <= 0)
5361 return iRowsFound == 0;
5363 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5365 map<int, pair<CStdString,int> > mapYears;
5366 map<int, pair<CStdString,int> >::iterator it;
5367 while (!m_pDS->eof())
5370 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5373 time.SetFromDateString(m_pDS->fv(0).get_asString());
5374 lYear = time.GetYear();
5376 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5377 lYear = m_pDS->fv(0).get_asInt();
5378 it = mapYears.find(lYear);
5379 if (it == mapYears.end())
5382 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5384 CStdString year = StringUtils::Format("%d", lYear);
5385 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5386 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5388 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5395 for (it=mapYears.begin();it != mapYears.end();++it)
5399 CFileItemPtr pItem(new CFileItem(it->second.first));
5401 CVideoDbUrl itemUrl = videoUrl;
5402 CStdString path = StringUtils::Format("%ld/", it->first);
5403 itemUrl.AppendPath(path);
5404 pItem->SetPath(itemUrl.ToString());
5406 pItem->m_bIsFolder=true;
5407 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5408 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5414 while (!m_pDS->eof())
5417 CStdString strLabel;
5418 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5421 time.SetFromDateString(m_pDS->fv(0).get_asString());
5422 lYear = time.GetYear();
5423 strLabel = StringUtils::Format("%i",lYear);
5425 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5427 lYear = m_pDS->fv(0).get_asInt();
5428 strLabel = m_pDS->fv(0).get_asString();
5435 CFileItemPtr pItem(new CFileItem(strLabel));
5437 CVideoDbUrl itemUrl = videoUrl;
5438 CStdString path = StringUtils::Format("%ld/", lYear);
5439 itemUrl.AppendPath(path);
5440 pItem->SetPath(itemUrl.ToString());
5442 pItem->m_bIsFolder=true;
5443 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5445 // fv(2) is the number of videos watched, fv(1) is the total number. We set the playcount
5446 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5447 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5450 // take care of dupes ..
5451 if (!items.Contains(pItem->GetPath()))
5463 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5468 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5472 if (NULL == m_pDB.get()) return false;
5473 if (NULL == m_pDS.get()) return false;
5475 // look for duplicate show titles and stack them into a list
5478 CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5479 CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5480 if (!m_pDS->query(strSQL.c_str())) return false;
5481 int iRows = m_pDS->num_rows();
5482 if (iRows == 0) return false; // this should never happen!
5484 { // more than one show, so stack them up
5486 while (!m_pDS->eof())
5488 strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5491 strIn[strIn.size() - 1] = ')'; // replace last , with )
5498 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5503 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5507 if (NULL == m_pDB.get()) return false;
5508 if (NULL == m_pDS.get()) return false;
5510 // parse the base path to get additional filters
5511 CVideoDbUrl videoUrl;
5512 if (!videoUrl.FromString(strBaseDir))
5515 CStdString strIn = PrepareSQL("= %i", idShow);
5516 GetStackedTvShowList(idShow, strIn);
5518 CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5520 "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5521 "seasons.idSeason, "
5522 "count(1), count(files.playCount) "
5523 "FROM episodeview ", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_PLOT, VIDEODB_ID_TV_PREMIERED, VIDEODB_ID_TV_GENRE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_MPAA);
5526 filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5527 "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5528 "JOIN files ON files.idFile = episodeview.idFile "
5529 "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5530 "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5531 filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5532 filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5534 videoUrl.AddOption("tvshowid", idShow);
5537 videoUrl.AddOption("actorid", idActor);
5538 else if (idDirector != -1)
5539 videoUrl.AddOption("directorid", idDirector);
5540 else if (idGenre != -1)
5541 videoUrl.AddOption("genreid", idGenre);
5542 else if (idYear != -1)
5543 videoUrl.AddOption("year", idYear);
5545 if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5548 int iRowsFound = RunQuery(strSQL);
5549 if (iRowsFound <= 0)
5550 return iRowsFound == 0;
5552 // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5553 CStdString showTitle = m_pDS->fv(2).get_asString();
5554 CStdString showPlot = m_pDS->fv(3).get_asString();
5555 CStdString showPremiered = m_pDS->fv(4).get_asString();
5556 CStdString showStudio = m_pDS->fv(6).get_asString();
5557 CStdString showMPAARating = m_pDS->fv(7).get_asString();
5559 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5561 map<int, CSeason> mapSeasons;
5562 map<int, CSeason>::iterator it;
5563 while (!m_pDS->eof())
5565 int iSeason = m_pDS->fv(0).get_asInt();
5566 it = mapSeasons.find(iSeason);
5568 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5573 if (it == mapSeasons.end())
5576 season.path = m_pDS->fv(1).get_asString();
5577 season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5578 season.id = m_pDS->fv(8).get_asInt();
5579 season.numEpisodes = m_pDS->fv(9).get_asInt();
5580 season.numWatched = m_pDS->fv(10).get_asInt();
5581 mapSeasons.insert(make_pair(iSeason, season));
5587 for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5589 int iSeason = it->first;
5590 CStdString strLabel;
5592 strLabel = g_localizeStrings.Get(20381);
5594 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5595 CFileItemPtr pItem(new CFileItem(strLabel));
5597 CVideoDbUrl itemUrl = videoUrl;
5598 CStdString strDir = StringUtils::Format("%ld/", it->first);
5599 itemUrl.AppendPath(strDir);
5600 pItem->SetPath(itemUrl.ToString());
5602 pItem->m_bIsFolder=true;
5603 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5604 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5605 pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5606 pItem->GetVideoInfoTag()->m_type = "season";
5607 pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5608 pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5609 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5610 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5611 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5612 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5613 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5614 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5615 pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5616 pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5617 pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5618 pItem->SetProperty("watchedepisodes", it->second.numWatched);
5619 pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5620 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5621 pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5622 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5628 while (!m_pDS->eof())
5630 int iSeason = m_pDS->fv(0).get_asInt();
5631 CStdString strLabel;
5633 strLabel = g_localizeStrings.Get(20381);
5635 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5636 CFileItemPtr pItem(new CFileItem(strLabel));
5638 CVideoDbUrl itemUrl = videoUrl;
5639 CStdString strDir = StringUtils::Format("%ld/", iSeason);
5640 itemUrl.AppendPath(strDir);
5641 pItem->SetPath(itemUrl.ToString());
5643 pItem->m_bIsFolder=true;
5644 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5645 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5646 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5647 pItem->GetVideoInfoTag()->m_type = "season";
5648 pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5649 pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5650 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5651 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5652 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5653 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5654 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5655 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5656 int totalEpisodes = m_pDS->fv(9).get_asInt();
5657 int watchedEpisodes = m_pDS->fv(10).get_asInt();
5658 pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5659 pItem->SetProperty("totalepisodes", totalEpisodes);
5660 pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5661 pItem->SetProperty("watchedepisodes", watchedEpisodes);
5662 pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5663 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5664 pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5665 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5672 // now add any linked movies
5673 if (getLinkedMovies)
5676 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5677 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5678 CFileItemList movieItems;
5679 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
5681 if (movieItems.Size() > 0)
5682 items.Append(movieItems);
5689 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5694 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5696 if (NULL == m_pDB.get() || NULL == m_pDS.get())
5699 if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5702 SortDescription sorting = sortDescription;
5703 if (sortDescription.sortBy == SortByFile ||
5704 sortDescription.sortBy == SortByTitle ||
5705 sortDescription.sortBy == SortBySortTitle ||
5706 sortDescription.sortBy == SortByLabel ||
5707 sortDescription.sortBy == SortByDateAdded ||
5708 sortDescription.sortBy == SortByRating ||
5709 sortDescription.sortBy == SortByYear ||
5710 sortDescription.sortBy == SortByLastPlayed ||
5711 sortDescription.sortBy == SortByPlaycount)
5712 sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5714 bool success = false;
5717 case MediaTypeMovie:
5718 success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5721 case MediaTypeTvShow:
5722 success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
5725 case MediaTypeEpisode:
5726 success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
5729 case MediaTypeMusicVideo:
5730 success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
5737 items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
5741 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5743 CVideoDbUrl videoUrl;
5744 if (!videoUrl.FromString(strBaseDir))
5747 return GetItems(strBaseDir, videoUrl.GetType(), videoUrl.GetItemType(), items, filter, sortDescription);
5750 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, const CStdString &mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5752 VIDEODB_CONTENT_TYPE contentType;
5753 if (mediaType.Equals("movies"))
5754 contentType = VIDEODB_CONTENT_MOVIES;
5755 else if (mediaType.Equals("tvshows"))
5757 if (itemType.Equals("episodes"))
5758 contentType = VIDEODB_CONTENT_EPISODES;
5760 contentType = VIDEODB_CONTENT_TVSHOWS;
5762 else if (mediaType.Equals("musicvideos"))
5763 contentType = VIDEODB_CONTENT_MUSICVIDEOS;
5767 return GetItems(strBaseDir, contentType, itemType, items, filter, sortDescription);
5770 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, VIDEODB_CONTENT_TYPE mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5772 if (itemType.Equals("movies") && (mediaType == VIDEODB_CONTENT_MOVIES || mediaType == VIDEODB_CONTENT_MOVIE_SETS))
5773 return GetMoviesByWhere(strBaseDir, filter, items, sortDescription);
5774 else if (itemType.Equals("tvshows") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5775 return GetTvShowsByWhere(strBaseDir, filter, items, sortDescription);
5776 else if (itemType.Equals("musicvideos") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5777 return GetMusicVideosByWhere(strBaseDir, filter, items, true, sortDescription);
5778 else if (itemType.Equals("episodes") && mediaType == VIDEODB_CONTENT_EPISODES)
5779 return GetEpisodesByWhere(strBaseDir, filter, items, true, sortDescription);
5780 else if (itemType.Equals("seasons") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5781 return GetSeasonsNav(strBaseDir, items);
5782 else if (itemType.Equals("genres"))
5783 return GetGenresNav(strBaseDir, items, mediaType, filter);
5784 else if (itemType.Equals("years"))
5785 return GetYearsNav(strBaseDir, items, mediaType, filter);
5786 else if (itemType.Equals("actors"))
5787 return GetActorsNav(strBaseDir, items, mediaType, filter);
5788 else if (itemType.Equals("directors"))
5789 return GetDirectorsNav(strBaseDir, items, mediaType, filter);
5790 else if (itemType.Equals("writers"))
5791 return GetWritersNav(strBaseDir, items, mediaType, filter);
5792 else if (itemType.Equals("studios"))
5793 return GetStudiosNav(strBaseDir, items, mediaType, filter);
5794 else if (itemType.Equals("sets"))
5795 return GetSetsNav(strBaseDir, items, mediaType, filter);
5796 else if (itemType.Equals("countries"))
5797 return GetCountriesNav(strBaseDir, items, mediaType, filter);
5798 else if (itemType.Equals("tags"))
5799 return GetTagsNav(strBaseDir, items, mediaType, filter);
5800 else if (itemType.Equals("artists") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5801 return GetActorsNav(strBaseDir, items, mediaType, filter);
5802 else if (itemType.Equals("albums") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5803 return GetMusicVideoAlbumsNav(strBaseDir, items, -1, filter);
5808 CStdString CVideoDatabase::GetItemById(const CStdString &itemType, int id)
5810 if (itemType.Equals("genres"))
5811 return GetGenreById(id);
5812 else if (itemType.Equals("years"))
5813 return StringUtils::Format("%d", id);
5814 else if (itemType.Equals("actors") || itemType.Equals("directors") || itemType.Equals("artists"))
5815 return GetPersonById(id);
5816 else if (itemType.Equals("studios"))
5817 return GetStudioById(id);
5818 else if (itemType.Equals("sets"))
5819 return GetSetById(id);
5820 else if (itemType.Equals("countries"))
5821 return GetCountryById(id);
5822 else if (itemType.Equals("tags"))
5823 return GetTagById(id);
5824 else if (itemType.Equals("albums"))
5825 return GetMusicVideoAlbumById(id);
5830 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
5831 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
5832 int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
5833 const SortDescription &sortDescription /* = SortDescription() */)
5835 CVideoDbUrl videoUrl;
5836 if (!videoUrl.FromString(strBaseDir))
5840 videoUrl.AddOption("genreid", idGenre);
5841 else if (idCountry > 0)
5842 videoUrl.AddOption("countryid", idCountry);
5843 else if (idStudio > 0)
5844 videoUrl.AddOption("studioid", idStudio);
5845 else if (idDirector > 0)
5846 videoUrl.AddOption("directorid", idDirector);
5847 else if (idYear > 0)
5848 videoUrl.AddOption("year", idYear);
5849 else if (idActor > 0)
5850 videoUrl.AddOption("actorid", idActor);
5852 videoUrl.AddOption("setid", idSet);
5854 videoUrl.AddOption("tagid", idTag);
5857 return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
5860 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5867 if (NULL == m_pDB.get()) return false;
5868 if (NULL == m_pDS.get()) return false;
5870 // parse the base path to get additional filters
5871 CVideoDbUrl videoUrl;
5872 Filter extFilter = filter;
5873 SortDescription sorting = sortDescription;
5874 if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
5879 CStdString strSQL = "select %s from movieview ";
5880 CStdString strSQLExtra;
5881 if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
5884 // Apply the limiting directly here if there's no special sorting but limiting
5885 if (extFilter.limit.empty() &&
5886 sorting.sortBy == SortByNone &&
5887 (sorting.limitStart > 0 || sorting.limitEnd > 0))
5889 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
5890 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
5893 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
5895 int iRowsFound = RunQuery(strSQL);
5896 if (iRowsFound <= 0)
5897 return iRowsFound == 0;
5899 // store the total value of items as a property
5900 if (total < iRowsFound)
5902 items.SetProperty("total", total);
5904 DatabaseResults results;
5905 results.reserve(iRowsFound);
5907 if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
5910 // get data from returned rows
5911 items.Reserve(results.size());
5912 const query_data &data = m_pDS->get_result_set().records;
5913 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
5915 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
5916 const dbiplus::sql_record* const record = data.at(targetRow);
5918 CVideoInfoTag movie = GetDetailsForMovie(record);
5919 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
5920 g_passwordManager.bMasterUser ||
5921 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
5923 CFileItemPtr pItem(new CFileItem(movie));
5925 CVideoDbUrl itemUrl = videoUrl;
5926 CStdString path = StringUtils::Format("%ld", movie.m_iDbId);
5927 itemUrl.AppendPath(path);
5928 pItem->SetPath(itemUrl.ToString());
5930 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
5941 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5946 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
5947 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
5948 const SortDescription &sortDescription /* = SortDescription() */)
5950 CVideoDbUrl videoUrl;
5951 if (!videoUrl.FromString(strBaseDir))
5955 videoUrl.AddOption("genreid", idGenre);
5956 else if (idStudio != -1)
5957 videoUrl.AddOption("studioid", idStudio);
5958 else if (idDirector != -1)
5959 videoUrl.AddOption("directorid", idDirector);
5960 else if (idYear != -1)
5961 videoUrl.AddOption("year", idYear);
5962 else if (idActor != -1)
5963 videoUrl.AddOption("actorid", idActor);
5964 else if (idTag != -1)
5965 videoUrl.AddOption("tagid", idTag);
5968 return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
5971 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5977 if (NULL == m_pDB.get()) return false;
5978 if (NULL == m_pDS.get()) return false;
5982 CStdString strSQL = "SELECT %s FROM tvshowview ";
5983 CVideoDbUrl videoUrl;
5984 CStdString strSQLExtra;
5985 Filter extFilter = filter;
5986 SortDescription sorting = sortDescription;
5987 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
5990 // Apply the limiting directly here if there's no special sorting but limiting
5991 if (extFilter.limit.empty() &&
5992 sorting.sortBy == SortByNone &&
5993 (sorting.limitStart > 0 || sorting.limitEnd > 0))
5995 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
5996 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
5999 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6001 int iRowsFound = RunQuery(strSQL);
6002 if (iRowsFound <= 0)
6003 return iRowsFound == 0;
6005 // store the total value of items as a property
6006 if (total < iRowsFound)
6008 items.SetProperty("total", total);
6010 DatabaseResults results;
6011 results.reserve(iRowsFound);
6012 if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
6015 // get data from returned rows
6016 items.Reserve(results.size());
6017 const query_data &data = m_pDS->get_result_set().records;
6018 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6020 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6021 const dbiplus::sql_record* const record = data.at(targetRow);
6023 CFileItemPtr pItem(new CFileItem());
6024 CVideoInfoTag movie = GetDetailsForTvShow(record, false, pItem.get());
6025 if ((CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6026 g_passwordManager.bMasterUser ||
6027 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video"))) &&
6028 (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
6030 pItem->SetFromVideoInfoTag(movie);
6032 CVideoDbUrl itemUrl = videoUrl;
6033 CStdString path = StringUtils::Format("%ld/", record->at(0).get_asInt());
6034 itemUrl.AppendPath(path);
6035 pItem->SetPath(itemUrl.ToString());
6037 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6042 Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6050 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6055 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6057 if (maintainSortOrder)
6059 // save current sort order
6060 for (int i = 0; i < items.Size(); i++)
6061 items[i]->m_iprogramCount = i;
6066 case VIDEODB_CONTENT_TVSHOWS:
6069 items.Sort(SortBySortTitle, SortOrderAscending);
6072 while (i < items.Size())
6074 CFileItemPtr pItem = items.Get(i);
6075 CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6076 CStdString strFanArt = pItem->GetArt("fanart");
6079 bool bStacked = false;
6080 while (j < items.Size())
6082 CFileItemPtr jItem = items.Get(j);
6084 // matching title? append information
6085 if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6087 if (jItem->GetVideoInfoTag()->m_premiered !=
6088 pItem->GetVideoInfoTag()->m_premiered)
6095 // increment episode counts
6096 pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6097 pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6098 pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6099 pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6100 pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6102 // adjust lastplayed
6103 if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6104 pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6106 // check for fanart if not already set
6107 if (strFanArt.empty())
6108 strFanArt = jItem->GetArt("fanart");
6110 // remove duplicate entry
6113 // no match? exit loop
6117 // update playcount and fanart
6120 pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6121 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6122 if (!strFanArt.empty())
6123 pItem->SetArt("fanart", strFanArt);
6125 // increment i to j which is the next item
6130 // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6131 // so that if we eventually want to stack during scan we can utilize it.
6133 case VIDEODB_CONTENT_EPISODES:
6135 // sort by ShowTitle, Episode, Filename
6136 items.Sort(SortByEpisodeNumber, SortOrderAscending);
6139 while (i < items.Size())
6141 CFileItemPtr pItem = items.Get(i);
6142 CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6143 int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6144 int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6145 //CStdString strFanArt = pItem->GetArt("fanart");
6147 // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6148 CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6149 bool bDvdFolder = StringUtils::EndsWithNoCase(strFileNameAndPath, "video_ts.ifo");
6151 vector<CStdString> paths;
6152 paths.push_back(strFileNameAndPath);
6153 CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6156 int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6157 while (j < items.Size())
6159 CFileItemPtr jItem = items.Get(j);
6160 const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6161 CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6163 CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6164 // compare path, season, episode
6167 jTag->m_strPath.Equals(strPath) &&
6168 jTag->m_iSeason == iSeason &&
6169 jTag->m_iEpisode == iEpisode
6172 // keep checking to see if this is dvd folder
6175 bDvdFolder = StringUtils::EndsWithNoCase(jFileNameAndPath, "video_ts.ifo");
6176 // if we have a dvd folder, we stack differently
6179 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6181 paths.push_back(jFileNameAndPath);
6185 // increment playcount
6186 iPlayCount += jTag->m_playCount;
6188 // episodes dont have fanart yet
6189 //if (strFanArt.empty())
6190 // strFanArt = jItem->GetArt("fanart");
6192 paths.push_back(jFileNameAndPath);
6196 // remove duplicate entry
6200 // no match? exit loop
6204 // update playcount and fanart if we have a stacked entry
6205 if (paths.size() > 1)
6207 CStackDirectory dir;
6208 CStdString strStack;
6209 dir.ConstructStackPath(paths, strStack);
6210 pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6211 pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6212 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6214 // episodes dont have fanart yet
6215 //if (!strFanArt.empty())
6216 // pItem->SetArt("fanart", strFanArt);
6218 // increment i to j which is the next item
6224 // stack other types later
6228 if (maintainSortOrder)
6230 // restore original sort order - essential for smartplaylists
6231 items.Sort(SortByProgramCount, SortOrderAscending);
6235 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6237 CVideoDbUrl videoUrl;
6238 if (!videoUrl.FromString(strBaseDir))
6244 strIn = PrepareSQL("= %i", idShow);
6245 GetStackedTvShowList(idShow, strIn);
6247 videoUrl.AddOption("tvshowid", idShow);
6249 videoUrl.AddOption("season", idSeason);
6252 videoUrl.AddOption("genreid", idGenre);
6253 else if (idYear !=-1)
6254 videoUrl.AddOption("year", idYear);
6255 else if (idActor != -1)
6256 videoUrl.AddOption("actorid", idActor);
6258 else if (idYear != -1)
6259 videoUrl.AddOption("year", idYear);
6261 if (idDirector != -1)
6262 videoUrl.AddOption("directorid", idDirector);
6265 bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6267 if (idSeason == -1 && idShow != -1)
6268 { // add any linked movies
6270 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6271 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6272 CFileItemList movieItems;
6273 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
6275 if (movieItems.Size() > 0)
6276 items.Append(movieItems);
6282 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6289 if (NULL == m_pDB.get()) return false;
6290 if (NULL == m_pDS.get()) return false;
6294 CStdString strSQL = "select %s from episodeview ";
6295 CVideoDbUrl videoUrl;
6296 CStdString strSQLExtra;
6297 Filter extFilter = filter;
6298 SortDescription sorting = sortDescription;
6299 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6302 // Apply the limiting directly here if there's no special sorting but limiting
6303 if (extFilter.limit.empty() &&
6304 sorting.sortBy == SortByNone &&
6305 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6307 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6308 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6311 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6313 int iRowsFound = RunQuery(strSQL);
6314 if (iRowsFound <= 0)
6315 return iRowsFound == 0;
6317 // store the total value of items as a property
6318 if (total < iRowsFound)
6320 items.SetProperty("total", total);
6322 DatabaseResults results;
6323 results.reserve(iRowsFound);
6324 if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6327 // get data from returned rows
6328 items.Reserve(results.size());
6329 CLabelFormatter formatter("%H. %T", "");
6331 const query_data &data = m_pDS->get_result_set().records;
6332 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6334 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6335 const dbiplus::sql_record* const record = data.at(targetRow);
6337 CVideoInfoTag movie = GetDetailsForEpisode(record);
6338 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6339 g_passwordManager.bMasterUser ||
6340 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6342 CFileItemPtr pItem(new CFileItem(movie));
6343 formatter.FormatLabel(pItem.get());
6345 int idEpisode = record->at(0).get_asInt();
6347 CVideoDbUrl itemUrl = videoUrl;
6349 if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6350 path = StringUtils::Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6352 path = StringUtils::Format("%ld", idEpisode);
6353 itemUrl.AppendPath(path);
6354 pItem->SetPath(itemUrl.ToString());
6356 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6357 pItem->m_dateTime = movie.m_firstAired;
6358 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6369 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6374 bool CVideoDatabase::GetMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idArtist, int idDirector, int idStudio, int idAlbum, int idTag /* = -1 */, const SortDescription &sortDescription /* = SortDescription() */)
6376 CVideoDbUrl videoUrl;
6377 if (!videoUrl.FromString(strBaseDir))
6381 videoUrl.AddOption("genreid", idGenre);
6382 else if (idStudio != -1)
6383 videoUrl.AddOption("studioid", idStudio);
6384 else if (idDirector != -1)
6385 videoUrl.AddOption("directorid", idDirector);
6386 else if (idYear !=-1)
6387 videoUrl.AddOption("year", idYear);
6388 else if (idArtist != -1)
6389 videoUrl.AddOption("artistid", idArtist);
6390 else if (idTag != -1)
6391 videoUrl.AddOption("tagid", idTag);
6393 videoUrl.AddOption("albumid", idAlbum);
6396 return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6399 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6402 filter.order = "dateAdded desc, idMovie desc";
6403 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6404 return GetMoviesByWhere(strBaseDir, filter, items);
6407 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6410 filter.order = "dateAdded desc, idEpisode desc";
6411 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6412 return GetEpisodesByWhere(strBaseDir, filter, items, false);
6415 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6418 filter.order = "dateAdded desc, idMVideo desc";
6419 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6420 return GetMusicVideosByWhere(strBaseDir, filter, items);
6423 CStdString CVideoDatabase::GetGenreById(int id)
6425 return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6428 CStdString CVideoDatabase::GetCountryById(int id)
6430 return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6433 CStdString CVideoDatabase::GetSetById(int id)
6435 return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6438 CStdString CVideoDatabase::GetTagById(int id)
6440 return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6443 CStdString CVideoDatabase::GetPersonById(int id)
6445 return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6448 CStdString CVideoDatabase::GetStudioById(int id)
6450 return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6453 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6455 return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6458 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6460 return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6463 bool CVideoDatabase::HasSets() const
6467 if (NULL == m_pDB.get()) return false;
6468 if (NULL == m_pDS.get()) return false;
6470 m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6471 "JOIN sets ON sets.idSet = movieview.idSet "
6472 "GROUP BY movieview.idSet HAVING c>1");
6474 bool bResult = (m_pDS->num_rows() > 0);
6480 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6485 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6489 if (NULL == m_pDB.get()) return false;
6490 if (NULL == m_pDS2.get()) return false;
6492 // make sure we use m_pDS2, as this is called in loops using m_pDS
6493 CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6494 m_pDS2->query( strSQL.c_str() );
6498 result=m_pDS2->fv(0).get_asInt();
6505 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6510 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6513 sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6514 CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6517 return atoi(id.c_str());
6520 bool CVideoDatabase::HasContent()
6522 return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6523 HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6524 HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6527 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6529 bool result = false;
6532 if (NULL == m_pDB.get()) return false;
6533 if (NULL == m_pDS.get()) return false;
6536 if (type == VIDEODB_CONTENT_MOVIES)
6537 sql = "select count(1) from movie";
6538 else if (type == VIDEODB_CONTENT_TVSHOWS)
6539 sql = "select count(1) from tvshow";
6540 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6541 sql = "select count(1) from musicvideo";
6542 m_pDS->query( sql.c_str() );
6545 result = (m_pDS->fv(0).get_asInt() > 0);
6551 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6556 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6560 if (NULL == m_pDB.get()) return 0;
6561 if (NULL == m_pDS.get()) return 0;
6563 CStdString strSQL = StringUtils::Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6564 m_pDS->query( strSQL.c_str() );
6568 iResult = m_pDS->fv("nummovies").get_asInt();
6575 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6580 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6582 SScanSettings settings;
6583 return GetScraperForPath(strPath, settings);
6586 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6589 return GetScraperForPath(strPath, settings, dummy);
6592 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6594 foundDirectly = false;
6597 if (strPath.empty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6600 CStdString strPath2;
6602 if (URIUtils::IsMultiPath(strPath))
6603 strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6607 CStdString strPath1 = URIUtils::GetDirectory(strPath2);
6608 int idPath = GetPathId(strPath1);
6612 CStdString strSQL=PrepareSQL("select path.strContent,path.strScraper,path.scanRecursive,path.useFolderNames,path.strSettings,path.noUpdate,path.exclude from path where path.idPath=%i",idPath);
6613 m_pDS->query( strSQL.c_str() );
6617 CONTENT_TYPE content = CONTENT_NONE;
6619 { // path is stored in db
6621 if (m_pDS->fv("path.exclude").get_asBool())
6623 settings.exclude = true;
6625 return ScraperPtr();
6627 settings.exclude = false;
6629 // try and ascertain scraper for this path
6630 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6631 StringUtils::ToLower(strcontent);
6632 content = TranslateContent(strcontent);
6634 //FIXME paths stored should not have empty strContent
6635 //assert(content != CONTENT_NONE);
6636 CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6639 if (!scraperID.empty() &&
6640 CAddonMgr::Get().GetAddon(scraperID, addon))
6642 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6644 return ScraperPtr();
6646 // store this path's content & settings
6647 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6648 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6649 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6650 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6654 if (content == CONTENT_NONE)
6655 { // this path is not saved in db
6656 // we must drill up until a scraper is configured
6657 CStdString strParent;
6658 while (URIUtils::GetParentPath(strPath1, strParent))
6662 CStdString strSQL=PrepareSQL("select path.strContent,path.strScraper,path.scanRecursive,path.useFolderNames,path.strSettings,path.noUpdate, path.exclude from path where strPath='%s'",strParent.c_str());
6663 m_pDS->query(strSQL.c_str());
6665 CONTENT_TYPE content = CONTENT_NONE;
6669 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6670 StringUtils::ToLower(strcontent);
6671 if (m_pDS->fv("path.exclude").get_asBool())
6673 settings.exclude = true;
6679 content = TranslateContent(strcontent);
6682 if (content != CONTENT_NONE &&
6683 CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6685 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6686 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6687 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6688 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6689 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6690 settings.exclude = false;
6694 strPath1 = strParent;
6699 if (!scraper || scraper->Content() == CONTENT_NONE)
6700 return ScraperPtr();
6702 if (scraper->Content() == CONTENT_TVSHOWS)
6704 settings.recurse = 0;
6705 if(settings.parent_name) // single show
6707 settings.parent_name_root = settings.parent_name = (iFound == 1);
6711 settings.parent_name_root = settings.parent_name = (iFound == 2);
6714 else if (scraper->Content() == CONTENT_MOVIES)
6716 settings.recurse = settings.recurse - (iFound-1);
6717 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6719 else if (scraper->Content() == CONTENT_MUSICVIDEOS)
6721 settings.recurse = settings.recurse - (iFound-1);
6722 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6727 return ScraperPtr();
6729 foundDirectly = (iFound == 1);
6734 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6736 return ScraperPtr();
6739 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
6741 SScanSettings settings;
6742 bool foundDirectly = false;
6743 ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
6746 if (scraper->Content() == CONTENT_TVSHOWS)
6747 { // check for episodes or seasons. Assumptions are:
6748 // 1. if episodes are in the path then we're in episodes.
6749 // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
6750 // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
6751 CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
6752 m_pDS->query( sql.c_str() );
6753 if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
6755 return foundDirectly ? "tvshows" : "seasons";
6757 return TranslateContent(scraper->Content());
6762 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
6768 if (NULL == m_pDB.get()) return;
6769 if (NULL == m_pDS.get()) return;
6771 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6772 strSQL=PrepareSQL("select genre.idGenre,genre.strGenre,path.strPath from genre,genrelinkmovie,movie,path,files where genre.idGenre=genrelinkmovie.idGenre and genrelinkmovie.idMovie=movie.idMovie and files.idFile=movie.idFile and path.idPath=files.idPath and genre.strGenre like '%%%s%%'",strSearch.c_str());
6774 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6775 m_pDS->query( strSQL.c_str() );
6777 while (!m_pDS->eof())
6779 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6780 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6781 *CMediaSourceSettings::Get().GetSources("video")))
6787 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6788 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6789 pItem->SetPath("videodb://movies/genres/"+ strDir);
6790 pItem->m_bIsFolder=true;
6798 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6802 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
6808 if (NULL == m_pDB.get()) return;
6809 if (NULL == m_pDS.get()) return;
6811 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6812 strSQL=PrepareSQL("select country.idCountry,country.strCountry,path.strPath from country,countrylinkmovie,movie,path,files where country.idCountry=countrylinkmovie.idCountry and countrylinkmovie.idMovie=movie.idMovie and files.idFile=movie.idFile and path.idPath=files.idPath and country.strCountry like '%%%s%%'",strSearch.c_str());
6814 strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
6815 m_pDS->query( strSQL.c_str() );
6817 while (!m_pDS->eof())
6819 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6820 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6821 *CMediaSourceSettings::Get().GetSources("video")))
6827 CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
6828 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
6829 pItem->SetPath("videodb://movies/genres/"+ strDir);
6830 pItem->m_bIsFolder=true;
6838 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6842 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
6848 if (NULL == m_pDB.get()) return;
6849 if (NULL == m_pDS.get()) return;
6851 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6852 strSQL=PrepareSQL("select genre.idGenre,genre.strGenre,path.strPath from genre,genrelinktvshow,tvshow,path,tvshowlinkpath where genre.idGenre=genrelinktvshow.idGenre and genrelinktvshow.idShow=tvshow.idShow and path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=tvshow.idShow and genre.strGenre like '%%%s%%'",strSearch.c_str());
6854 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6855 m_pDS->query( strSQL.c_str() );
6857 while (!m_pDS->eof())
6859 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6860 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6866 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6867 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6868 pItem->SetPath("videodb://tvshows/genres/"+ strDir);
6869 pItem->m_bIsFolder=true;
6877 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6881 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
6887 if (NULL == m_pDB.get()) return;
6888 if (NULL == m_pDS.get()) return;
6890 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6891 strSQL=PrepareSQL("select actors.idActor,actors.strActor,path.strPath from actorlinkmovie,actors,movie,files,path where actors.idActor=actorlinkmovie.idActor and actorlinkmovie.idMovie=movie.idMovie and files.idFile=movie.idFile and files.idPath=path.idPath and actors.strActor like '%%%s%%'",strSearch.c_str());
6893 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from actorlinkmovie,actors,movie where actors.idActor=actorlinkmovie.idActor and actorlinkmovie.idMovie=movie.idMovie and actors.strActor like '%%%s%%'",strSearch.c_str());
6894 m_pDS->query( strSQL.c_str() );
6896 while (!m_pDS->eof())
6898 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6899 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6905 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6906 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6907 pItem->SetPath("videodb://movies/actors/"+ strDir);
6908 pItem->m_bIsFolder=true;
6916 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6920 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
6926 if (NULL == m_pDB.get()) return;
6927 if (NULL == m_pDS.get()) return;
6929 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6930 strSQL=PrepareSQL("select actors.idActor,actors.strActor,path.strPath from actorlinktvshow,actors,tvshow,path,tvshowlinkpath where actors.idActor=actorlinktvshow.idActor and actorlinktvshow.idShow=tvshow.idShow and tvshowlinkpath.idPath=tvshow.idShow and tvshowlinkpath.idPath=path.idPath and actors.strActor like '%%%s%%'",strSearch.c_str());
6932 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from actorlinktvshow,actors,tvshow where actors.idActor=actorlinktvshow.idActor and actorlinktvshow.idShow=tvshow.idShow and actors.strActor like '%%%s%%'",strSearch.c_str());
6933 m_pDS->query( strSQL.c_str() );
6935 while (!m_pDS->eof())
6937 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6938 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6944 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6945 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6946 pItem->SetPath("videodb://tvshows/actors/"+ strDir);
6947 pItem->m_bIsFolder=true;
6955 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6959 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
6965 if (NULL == m_pDB.get()) return;
6966 if (NULL == m_pDS.get()) return;
6969 if (!strSearch.empty())
6970 strLike = "and actors.strActor like '%%%s%%'";
6971 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6972 strSQL=PrepareSQL("select actors.idActor,actors.strActor,path.strPath from artistlinkmusicvideo,actors,musicvideo,files,path where actors.idActor=artistlinkmusicvideo.idArtist and artistlinkmusicvideo.idMVideo=musicvideo.idMVideo and files.idFile=musicvideo.idFile and files.idPath=path.idPath "+strLike,strSearch.c_str());
6974 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
6975 m_pDS->query( strSQL.c_str() );
6977 while (!m_pDS->eof())
6979 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6980 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6986 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6987 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6988 pItem->SetPath("videodb://musicvideos/artists/"+ strDir);
6989 pItem->m_bIsFolder=true;
6997 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7001 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
7007 if (NULL == m_pDB.get()) return;
7008 if (NULL == m_pDS.get()) return;
7010 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7011 strSQL=PrepareSQL("select genre.idGenre,genre.strGenre,path.strPath from genre,genrelinkmusicvideo,musicvideo,path,files where genre.idGenre=genrelinkmusicvideo.idGenre and genrelinkmusicvideo.idMVideo = musicvideo.idMVideo and files.idFile=musicvideo.idFile and path.idPath=files.idPath and genre.strGenre like '%%%s%%'",strSearch.c_str());
7013 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
7014 m_pDS->query( strSQL.c_str() );
7016 while (!m_pDS->eof())
7018 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7019 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7025 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7026 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7027 pItem->SetPath("videodb://musicvideos/genres/"+ strDir);
7028 pItem->m_bIsFolder=true;
7036 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7040 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7046 if (NULL == m_pDB.get()) return;
7047 if (NULL == m_pDS.get()) return;
7049 strSQL = StringUtils::Format("SELECT DISTINCT"
7050 " musicvideo.c%02d,"
7051 " musicvideo.idMVideo,"
7056 " files.idFile=musicvideo.idFile"
7058 " path.idPath=files.idPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
7059 if (!strSearch.empty())
7060 strSQL += PrepareSQL(" WHERE musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM, strSearch.c_str());
7062 m_pDS->query( strSQL.c_str() );
7064 while (!m_pDS->eof())
7066 if (m_pDS->fv(0).get_asString().empty())
7072 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7073 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7079 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7080 CStdString strDir = StringUtils::Format("%ld", m_pDS->fv(1).get_asInt());
7081 pItem->SetPath("videodb://musicvideos/titles/"+ strDir);
7082 pItem->m_bIsFolder=false;
7090 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7094 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7100 if (NULL == m_pDB.get()) return;
7101 if (NULL == m_pDS.get()) return;
7103 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7104 strSQL = PrepareSQL("select musicvideo.idMVideo,musicvideo.c%02d,musicvideo.c%02d,path.strPath from musicvideo,files,path where files.idFile=musicvideo.idFile and files.idPath=path.idPath and musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM,VIDEODB_ID_MUSICVIDEO_TITLE,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7106 strSQL = PrepareSQL("select musicvideo.idMVideo,musicvideo.c%02d,musicvideo.c%02d from musicvideo where musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM,VIDEODB_ID_MUSICVIDEO_TITLE,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7107 m_pDS->query( strSQL.c_str() );
7109 while (!m_pDS->eof())
7111 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7112 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7118 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7119 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7121 pItem->SetPath("videodb://"+ strDir);
7122 pItem->m_bIsFolder=false;
7130 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7134 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7141 if (NULL == m_pDB.get()) return false;
7142 if (NULL == m_pDS.get()) return false;
7146 CStdString strSQL = "select %s from musicvideoview ";
7147 CVideoDbUrl videoUrl;
7148 CStdString strSQLExtra;
7149 Filter extFilter = filter;
7150 SortDescription sorting = sortDescription;
7151 if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7154 // Apply the limiting directly here if there's no special sorting but limiting
7155 if (extFilter.limit.empty() &&
7156 sorting.sortBy == SortByNone &&
7157 (sorting.limitStart > 0 || sorting.limitEnd > 0))
7159 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7160 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7163 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7165 int iRowsFound = RunQuery(strSQL);
7166 if (iRowsFound <= 0)
7167 return iRowsFound == 0;
7169 // store the total value of items as a property
7170 if (total < iRowsFound)
7172 items.SetProperty("total", total);
7174 DatabaseResults results;
7175 results.reserve(iRowsFound);
7176 if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7179 // get data from returned rows
7180 items.Reserve(results.size());
7181 // get songs from returned subtable
7182 const query_data &data = m_pDS->get_result_set().records;
7183 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7185 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7186 const dbiplus::sql_record* const record = data.at(targetRow);
7188 CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7189 if (!checkLocks || CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7190 g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
7192 CFileItemPtr item(new CFileItem(musicvideo));
7194 CVideoDbUrl itemUrl = videoUrl;
7195 CStdString path = StringUtils::Format("%ld", record->at(0).get_asInt());
7196 itemUrl.AppendPath(path);
7197 item->SetPath(itemUrl.ToString());
7199 item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7210 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7215 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7219 if (NULL == m_pDB.get()) return 0;
7220 if (NULL == m_pDS.get()) return 0;
7222 CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7223 if (!m_pDS->query(strSQL.c_str())) return 0;
7225 if (m_pDS->num_rows() == 0)
7230 songIDs.reserve(m_pDS->num_rows());
7231 while (!m_pDS->eof())
7233 songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7237 return songIDs.size();
7241 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7246 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7252 if (NULL == m_pDB.get()) return false;
7253 if (NULL == m_pDS.get()) return false;
7255 // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7256 CStdString strSQL = StringUtils::Format("select * from musicvideoview where %s", strWhere.c_str());
7257 strSQL += PrepareSQL(" order by RANDOM() limit 1");
7258 CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7260 if (!m_pDS->query(strSQL.c_str()))
7262 int iRowsFound = m_pDS->num_rows();
7263 if (iRowsFound != 1)
7268 *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7269 CStdString path = StringUtils::Format("videodb://musicvideos/titles/%ld",item->GetVideoInfoTag()->m_iDbId);
7270 item->SetPath(path);
7271 idSong = m_pDS->fv("idMVideo").get_asInt();
7272 item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7278 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7283 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7287 if (NULL == m_pDB.get()) return -1;
7288 if (NULL == m_pDS.get()) return -1;
7291 if (strAlbum.empty() && strTitle.empty())
7292 { // we want to return matching artists only
7293 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7294 strSQL=PrepareSQL("select distinct actors.idActor,path.strPath from artistlinkmusicvideo,actors,musicvideo,files,path where actors.idActor=artistlinkmusicvideo.idArtist and artistlinkmusicvideo.idMVideo=musicvideo.idMVideo and files.idFile=musicvideo.idFile and files.idPath=path.idPath and actors.strActor like '%s'",strArtist.c_str());
7296 strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7299 { // we want to return the matching musicvideo
7300 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7301 strSQL = PrepareSQL("select musicvideo.idMVideo from musicvideo,files,path,artistlinkmusicvideo,actors where files.idFile=musicvideo.idFile and files.idPath=path.idPath and musicvideo.c%02d like '%s' and musicvideo.c%02d like '%s' and artistlinkmusicvideo.idMVideo=musicvideo.idMVideo and artistlinkmusicvideo.idArtist=actors.idActor and actors.strActor like '%s'",VIDEODB_ID_MUSICVIDEO_ALBUM,strAlbum.c_str(),VIDEODB_ID_MUSICVIDEO_TITLE,strTitle.c_str(),strArtist.c_str());
7303 strSQL = PrepareSQL("select musicvideo.idMVideo from musicvideo join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideo.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist where musicvideo.c%02d like '%s' and musicvideo.c%02d like '%s' and actors.strActor like '%s'",VIDEODB_ID_MUSICVIDEO_ALBUM,strAlbum.c_str(),VIDEODB_ID_MUSICVIDEO_TITLE,strTitle.c_str(),strArtist.c_str());
7305 m_pDS->query( strSQL.c_str() );
7310 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7311 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7317 int lResult = m_pDS->fv(0).get_asInt();
7323 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7328 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7334 if (NULL == m_pDB.get()) return;
7335 if (NULL == m_pDS.get()) return;
7337 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7338 strSQL = PrepareSQL("select movie.idMovie,movie.c%02d,path.strPath, movie.idSet from movie,files,path where files.idFile=movie.idFile and files.idPath=path.idPath and movie.c%02d like '%%%s%%'",VIDEODB_ID_TITLE,VIDEODB_ID_TITLE,strSearch.c_str());
7340 strSQL = PrepareSQL("select movie.idMovie,movie.c%02d, movie.idSet from movie where movie.c%02d like '%%%s%%'",VIDEODB_ID_TITLE,VIDEODB_ID_TITLE,strSearch.c_str());
7341 m_pDS->query( strSQL.c_str() );
7343 while (!m_pDS->eof())
7345 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7346 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7352 int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7353 int setId = m_pDS->fv("movie.idSet").get_asInt();
7354 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7356 if (setId <= 0 || !CSettings::Get().GetBool("videolibrary.groupmoviesets"))
7357 path = StringUtils::Format("videodb://movies/titles/%i", movieId);
7359 path = StringUtils::Format("videodb://movies/sets/%i/%i", setId, movieId);
7360 pItem->SetPath(path);
7361 pItem->m_bIsFolder=false;
7369 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7373 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7379 if (NULL == m_pDB.get()) return;
7380 if (NULL == m_pDS.get()) return;
7382 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7383 strSQL = PrepareSQL("select tvshow.idShow,tvshow.c%02d,path.strPath from tvshow,path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and tvshowlinkpath.idShow=tvshow.idShow and tvshow.c%02d like '%%%s%%'",VIDEODB_ID_TV_TITLE,VIDEODB_ID_TV_TITLE,strSearch.c_str());
7385 strSQL = PrepareSQL("select tvshow.idShow,tvshow.c%02d from tvshow where tvshow.c%02d like '%%%s%%'",VIDEODB_ID_TV_TITLE,VIDEODB_ID_TV_TITLE,strSearch.c_str());
7386 m_pDS->query( strSQL.c_str() );
7388 while (!m_pDS->eof())
7390 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7391 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7397 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7398 CStdString strDir = StringUtils::Format("tvshows/titles/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7400 pItem->SetPath("videodb://"+ strDir);
7401 pItem->m_bIsFolder=true;
7402 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7410 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7414 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7420 if (NULL == m_pDB.get()) return;
7421 if (NULL == m_pDS.get()) return;
7423 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7424 strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,episode.idShow,tvshow.c%02d,path.strPath from episode,files,path,tvshow where files.idFile=episode.idFile and episode.idShow=tvshow.idShow and files.idPath=path.idPath and episode.c%02d like '%%%s%%'",VIDEODB_ID_EPISODE_TITLE,VIDEODB_ID_EPISODE_SEASON,VIDEODB_ID_TV_TITLE,VIDEODB_ID_EPISODE_TITLE,strSearch.c_str());
7426 strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,episode.idShow,tvshow.c%02d from episode,tvshow where tvshow.idShow=episode.idShow and episode.c%02d like '%%%s%%'",VIDEODB_ID_EPISODE_TITLE,VIDEODB_ID_EPISODE_SEASON,VIDEODB_ID_TV_TITLE,VIDEODB_ID_EPISODE_TITLE,strSearch.c_str());
7427 m_pDS->query( strSQL.c_str() );
7429 while (!m_pDS->eof())
7431 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7432 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7438 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7439 CStdString path = StringUtils::Format("videodb://tvshows/titles/%ld/%ld/%ld",m_pDS->fv("episode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
7440 pItem->SetPath(path);
7441 pItem->m_bIsFolder=false;
7449 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7453 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7455 // Alternative searching - not quite as fast though due to
7456 // retrieving all information
7457 // Filter filter(PrepareSQL("c%02d like '%s%%' or c%02d like '%% %s%%'", VIDEODB_ID_MUSICVIDEO_TITLE, strSearch.c_str(), VIDEODB_ID_MUSICVIDEO_TITLE, strSearch.c_str()));
7458 // GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
7463 if (NULL == m_pDB.get()) return;
7464 if (NULL == m_pDS.get()) return;
7466 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7467 strSQL = PrepareSQL("select musicvideo.idMVideo,musicvideo.c%02d,path.strPath from musicvideo,files,path where files.idFile=musicvideo.idFile and files.idPath=path.idPath and musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_TITLE,VIDEODB_ID_MUSICVIDEO_TITLE,strSearch.c_str());
7469 strSQL = PrepareSQL("select musicvideo.idMVideo,musicvideo.c%02d from musicvideo where musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_TITLE,VIDEODB_ID_MUSICVIDEO_TITLE,strSearch.c_str());
7470 m_pDS->query( strSQL.c_str() );
7472 while (!m_pDS->eof())
7474 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7475 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7481 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7482 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7484 pItem->SetPath("videodb://"+ strDir);
7485 pItem->m_bIsFolder=false;
7493 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7497 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7499 // Alternative searching - not quite as fast though due to
7500 // retrieving all information
7502 // filter.where = PrepareSQL("c%02d like '%s%%' or c%02d like '%% %s%%'", VIDEODB_ID_EPISODE_PLOT, strSearch.c_str(), VIDEODB_ID_EPISODE_PLOT, strSearch.c_str());
7503 // filter.where += PrepareSQL("or c%02d like '%s%%' or c%02d like '%% %s%%'", VIDEODB_ID_EPISODE_TITLE, strSearch.c_str(), VIDEODB_ID_EPISODE_TITLE, strSearch.c_str());
7504 // GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
7510 if (NULL == m_pDB.get()) return;
7511 if (NULL == m_pDS.get()) return;
7513 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7514 strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,episode.idShow,tvshow.c%02d,path.strPath from episode,files,path,tvshow where files.idFile=episode.idFile and files.idPath=path.idPath and tvshow.idShow=episode.idShow and episode.c%02d like '%%%s%%'",VIDEODB_ID_EPISODE_TITLE,VIDEODB_ID_EPISODE_SEASON,VIDEODB_ID_TV_TITLE,VIDEODB_ID_EPISODE_PLOT,strSearch.c_str());
7516 strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,episode.idShow,tvshow.c%02d from episode,tvshow where tvshow.idShow=episode.idShow and episode.c%02d like '%%%s%%'",VIDEODB_ID_EPISODE_TITLE,VIDEODB_ID_EPISODE_SEASON,VIDEODB_ID_TV_TITLE,VIDEODB_ID_EPISODE_PLOT,strSearch.c_str());
7517 m_pDS->query( strSQL.c_str() );
7519 while (!m_pDS->eof())
7521 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7522 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7528 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7529 CStdString path = StringUtils::Format("videodb://tvshows/titles/%ld/%ld/%ld",m_pDS->fv("episode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
7530 pItem->SetPath(path);
7531 pItem->m_bIsFolder=false;
7539 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7543 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7549 if (NULL == m_pDB.get()) return;
7550 if (NULL == m_pDS.get()) return;
7552 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7553 strSQL = PrepareSQL("select movie.idMovie, movie.c%02d, path.strPath from movie,files,path where files.idFile=movie.idFile and files.idPath=path.idPath and (movie.c%02d like '%%%s%%' or movie.c%02d like '%%%s%%' or movie.c%02d like '%%%s%%')",VIDEODB_ID_TITLE,VIDEODB_ID_PLOT,strSearch.c_str(),VIDEODB_ID_PLOTOUTLINE,strSearch.c_str(),VIDEODB_ID_TAGLINE,strSearch.c_str());
7555 strSQL = PrepareSQL("select movie.idMovie, movie.c%02d from movie where (movie.c%02d like '%%%s%%' or movie.c%02d like '%%%s%%' or movie.c%02d like '%%%s%%')",VIDEODB_ID_TITLE,VIDEODB_ID_PLOT,strSearch.c_str(),VIDEODB_ID_PLOTOUTLINE,strSearch.c_str(),VIDEODB_ID_TAGLINE,strSearch.c_str());
7557 m_pDS->query( strSQL.c_str() );
7559 while (!m_pDS->eof())
7561 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7562 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7568 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7569 CStdString path = StringUtils::Format("videodb://movies/titles/%ld", m_pDS->fv(0).get_asInt());
7570 pItem->SetPath(path);
7571 pItem->m_bIsFolder=false;
7581 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7585 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7591 if (NULL == m_pDB.get()) return;
7592 if (NULL == m_pDS.get()) return;
7594 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7595 strSQL = PrepareSQL("select distinct directorlinkmovie.idDirector,actors.strActor,path.strPath from movie,files,path,actors,directorlinkmovie where files.idFile=movie.idFile and files.idPath=path.idPath and directorlinkmovie.idMovie=movie.idMovie and directorlinkmovie.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7597 strSQL = PrepareSQL("select distinct directorlinkmovie.idDirector,actors.strActor from movie,actors,directorlinkmovie where directorlinkmovie.idMovie=movie.idMovie and directorlinkmovie.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7599 m_pDS->query( strSQL.c_str() );
7601 while (!m_pDS->eof())
7603 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7604 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7610 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7611 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7613 pItem->SetPath("videodb://movies/directors/"+ strDir);
7614 pItem->m_bIsFolder=true;
7622 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7626 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7632 if (NULL == m_pDB.get()) return;
7633 if (NULL == m_pDS.get()) return;
7635 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7636 strSQL = PrepareSQL("select distinct directorlinktvshow.idDirector,actors.strActor,path.strPath from tvshow,path,actors,directorlinktvshow,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and tvshowlinkpath.idShow=tvshow.idShow and directorlinktvshow.idShow=tvshow.idShow and directorlinktvshow.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7638 strSQL = PrepareSQL("select distinct directorlinktvshow.idDirector,actors.strActor from tvshow,actors,directorlinktvshow where directorlinktvshow.idShow=tvshow.idShow and directorlinktvshow.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7640 m_pDS->query( strSQL.c_str() );
7642 while (!m_pDS->eof())
7644 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7645 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7651 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7652 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7654 pItem->SetPath("videodb://tvshows/studios/"+ strDir);
7655 pItem->m_bIsFolder=true;
7663 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7667 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7673 if (NULL == m_pDB.get()) return;
7674 if (NULL == m_pDS.get()) return;
7676 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7677 strSQL = PrepareSQL("select distinct directorlinkmusicvideo.idDirector,actors.strActor,path.strPath from musicvideo,files,path,actors,directorlinkmusicvideo where files.idFile=musicvideo.idFile and files.idPath=path.idPath and directorlinkmusicvideo.idMVideo=musicvideo.idMVideo and directorlinkmusicvideo.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7679 strSQL = PrepareSQL("select distinct directorlinkmusicvideo.idDirector,actors.strActor from musicvideo,actors,directorlinkmusicvideo where directorlinkmusicvideo.idMVideo=musicvideo.idMVideo and directorlinkmusicvideo.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7681 m_pDS->query( strSQL.c_str() );
7683 while (!m_pDS->eof())
7685 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7686 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7692 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7693 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7695 pItem->SetPath("videodb://musicvideos/albums/"+ strDir);
7696 pItem->m_bIsFolder=true;
7704 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7708 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7710 CGUIDialogProgress *progress=NULL;
7713 if (NULL == m_pDB.get()) return;
7714 if (NULL == m_pDS.get()) return;
7716 unsigned int time = XbmcThreads::SystemClockMillis();
7717 CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
7718 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
7722 // find all the files
7723 CStdString sql = "SELECT files.idFile, files.strFileName, path.strPath FROM files, path WHERE files.idPath = path.idPath";
7728 RollbackTransaction();
7729 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7733 CStdString strPaths;
7734 for (std::set<int>::const_iterator it = paths->begin(); it != paths->end(); ++it)
7735 strPaths += StringUtils::Format(",%i", *it);
7736 sql += PrepareSQL(" AND path.idPath IN (%s)", strPaths.substr(1).c_str());
7739 m_pDS->query(sql.c_str());
7740 if (m_pDS->num_rows() == 0) return;
7744 handle->SetTitle(g_localizeStrings.Get(700));
7745 handle->SetText("");
7747 else if (showProgress)
7749 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
7752 progress->SetHeading(700);
7753 progress->SetLine(0, "");
7754 progress->SetLine(1, 313);
7755 progress->SetLine(2, 330);
7756 progress->SetPercentage(0);
7757 progress->StartModal();
7758 progress->ShowProgressBar(true);
7762 std::string filesToTestForDelete;
7764 int total = m_pDS->num_rows();
7767 while (!m_pDS->eof())
7769 std::string path = m_pDS->fv("path.strPath").get_asString();
7770 std::string fileName = m_pDS->fv("files.strFileName").get_asString();
7771 CStdString fullPath;
7772 ConstructPath(fullPath, path, fileName);
7774 // get the first stacked file
7775 if (URIUtils::IsStack(fullPath))
7776 fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
7778 // remove optical, non-existing files
7779 if (URIUtils::IsOnDVD(fullPath) || !CFile::Exists(fullPath, false))
7780 filesToTestForDelete += m_pDS->fv("files.idFile").get_asString() + ",";
7782 if (handle == NULL && progress != NULL)
7784 int percentage = current * 100 / total;
7785 if (percentage > progress->GetPercentage())
7787 progress->SetPercentage(percentage);
7788 progress->Progress();
7790 if (progress->IsCanceled())
7794 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7798 else if (handle != NULL)
7799 handle->SetPercentage(current * 100 / (float)total);
7806 std::string filesToDelete;
7808 // Add any files that don't have a valid idPath entry to the filesToDelete list.
7809 m_pDS->query("SELECT files.idFile FROM files WHERE NOT EXISTS (SELECT 1 FROM path WHERE path.idPath = files.idPath)");
7810 while (!m_pDS->eof())
7812 string file = m_pDS->fv("files.idFile").get_asString() + ",";
7813 filesToTestForDelete += file;
7814 filesToDelete += file;
7820 std::map<int, bool> pathsDeleteDecisions;
7821 std::vector<int> movieIDs;
7822 std::vector<int> tvshowIDs;
7823 std::vector<int> episodeIDs;
7824 std::vector<int> musicVideoIDs;
7826 if (!filesToTestForDelete.empty())
7828 StringUtils::TrimRight(filesToTestForDelete, ",");
7830 movieIDs = CleanMediaType("movie", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7831 episodeIDs = CleanMediaType("episode", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7832 musicVideoIDs = CleanMediaType("musicvideo", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7835 if (progress != NULL)
7837 progress->SetPercentage(100);
7838 progress->Progress();
7841 if (!filesToDelete.empty())
7843 filesToDelete = "(" + StringUtils::TrimRight(filesToDelete, ",") + ")";
7845 CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
7846 sql = "DELETE FROM files WHERE idFile IN " + filesToDelete;
7847 m_pDS->exec(sql.c_str());
7849 CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
7850 sql = "DELETE FROM streamdetails WHERE idFile IN " + filesToDelete;
7851 m_pDS->exec(sql.c_str());
7853 CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
7854 sql = "DELETE FROM bookmark WHERE idFile IN " + filesToDelete;
7855 m_pDS->exec(sql.c_str());
7857 CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
7858 sql = "DELETE FROM settings WHERE idFile IN " + filesToDelete;
7859 m_pDS->exec(sql.c_str());
7861 CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
7862 sql = "DELETE FROM stacktimes WHERE idFile IN " + filesToDelete;
7863 m_pDS->exec(sql.c_str());
7866 if (!movieIDs.empty())
7868 std::string moviesToDelete;
7869 for (std::vector<int>::const_iterator it = movieIDs.begin(); it != movieIDs.end(); ++it)
7870 moviesToDelete += StringUtils::Format("%i,", *it);
7871 moviesToDelete = "(" + StringUtils::TrimRight(moviesToDelete, ",") + ")";
7873 CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
7874 sql = "DELETE FROM movie WHERE idMovie IN " + moviesToDelete;
7875 m_pDS->exec(sql.c_str());
7877 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
7878 sql = "DELETE FROM actorlinkmovie WHERE idMovie IN " + moviesToDelete;
7879 m_pDS->exec(sql.c_str());
7881 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
7882 sql = "DELETE FROM directorlinkmovie WHERE idMovie IN " + moviesToDelete;
7883 m_pDS->exec(sql.c_str());
7885 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
7886 sql = "DELETE FROM writerlinkmovie WHERE idMovie IN " + moviesToDelete;
7887 m_pDS->exec(sql.c_str());
7889 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
7890 sql = "DELETE FROM genrelinkmovie WHERE idMovie IN " + moviesToDelete;
7891 m_pDS->exec(sql.c_str());
7893 CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
7894 sql = "DELETE FROM countrylinkmovie WHERE idMovie IN " + moviesToDelete;
7895 m_pDS->exec(sql.c_str());
7897 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
7898 sql = "DELETE FROM studiolinkmovie WHERE idMovie IN " + moviesToDelete;
7899 m_pDS->exec(sql.c_str());
7902 if (!episodeIDs.empty())
7904 std::string episodesToDelete;
7905 for (std::vector<int>::const_iterator it = episodeIDs.begin(); it != episodeIDs.end(); ++it)
7906 episodesToDelete += StringUtils::Format("%i,", *it);
7907 episodesToDelete = "(" + StringUtils::TrimRight(episodesToDelete, ",") + ")";
7909 CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
7910 sql = "DELETE FROM episode WHERE idEpisode IN " + episodesToDelete;
7911 m_pDS->exec(sql.c_str());
7913 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
7914 sql = "DELETE FROM actorlinkepisode WHERE idEpisode IN " + episodesToDelete;
7915 m_pDS->exec(sql.c_str());
7917 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
7918 sql = "DELETE FROM directorlinkepisode WHERE idEpisode IN " + episodesToDelete;
7919 m_pDS->exec(sql.c_str());
7921 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
7922 sql = "DELETE FROM writerlinkepisode WHERE idEpisode IN " + episodesToDelete;
7923 m_pDS->exec(sql.c_str());
7926 CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and have content set...", __FUNCTION__);
7927 sql = "SELECT path.idPath, path.strPath FROM path "
7928 "WHERE NOT ((strContent IS NULL OR strContent = '') "
7929 "AND (strSettings IS NULL OR strSettings = '') "
7930 "AND (strHash IS NULL OR strHash = '') "
7931 "AND (exclude IS NULL OR exclude != 1))";
7932 m_pDS->query(sql.c_str());
7934 while (!m_pDS->eof())
7936 std::map<int, bool>::const_iterator pathsDeleteDecision = pathsDeleteDecisions.find(m_pDS->fv(0).get_asInt());
7937 if ((pathsDeleteDecision != pathsDeleteDecisions.end() && pathsDeleteDecision->second) ||
7938 (pathsDeleteDecision == pathsDeleteDecisions.end() && !CDirectory::Exists(m_pDS->fv(1).get_asString(), false)))
7939 strIds += m_pDS->fv(0).get_asString() + ",";
7945 if (!strIds.empty())
7947 sql = PrepareSQL("DELETE FROM path WHERE idPath IN (%s)", StringUtils::TrimRight(strIds, ",").c_str());
7948 m_pDS->exec(sql.c_str());
7949 sql = "DELETE FROM tvshowlinkpath WHERE NOT EXISTS (SELECT 1 FROM path WHERE path.idPath = tvshowlinkpath.idPath)";
7950 m_pDS->exec(sql.c_str());
7953 CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
7954 sql = "DELETE FROM tvshow WHERE NOT EXISTS (SELECT 1 FROM tvshowlinkpath WHERE tvshowlinkpath.idShow = tvshow.idShow)";
7955 m_pDS->exec(sql.c_str());
7957 std::string tvshowsToDelete;
7958 sql = "SELECT tvshow.idShow FROM tvshow "
7959 "JOIN tvshowlinkpath ON tvshow.idShow = tvshowlinkpath.idShow "
7960 "JOIN path ON path.idPath = tvshowlinkpath.idPath "
7961 "WHERE NOT EXISTS (SELECT 1 FROM episode WHERE episode.idShow = tvshow.idShow) "
7962 "AND (path.strContent IS NULL OR path.strContent = '')";
7963 m_pDS->query(sql.c_str());
7964 while (!m_pDS->eof())
7966 tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
7967 tvshowsToDelete += m_pDS->fv(0).get_asString() + ",";
7971 if (!tvshowsToDelete.empty())
7973 sql = "DELETE FROM tvshow WHERE idShow IN (" + StringUtils::TrimRight(tvshowsToDelete, ",") + ")";
7974 m_pDS->exec(sql.c_str());
7976 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
7977 sql = "DELETE FROM actorlinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = actorlinktvshow.idShow)";
7978 m_pDS->exec(sql.c_str());
7980 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
7981 sql = "DELETE FROM directorlinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = directorlinktvshow.idShow)";
7982 m_pDS->exec(sql.c_str());
7984 CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
7985 sql = "DELETE FROM tvshowlinkpath WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = tvshowlinkpath.idShow)";
7986 m_pDS->exec(sql.c_str());
7988 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
7989 sql = "DELETE FROM genrelinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = genrelinktvshow.idShow)";
7990 m_pDS->exec(sql.c_str());
7992 CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
7993 sql = "DELETE FROM seasons WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = seasons.idShow)";
7994 m_pDS->exec(sql.c_str());
7996 CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
7997 sql = "DELETE FROM movielinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = movielinktvshow.idShow)";
7998 m_pDS->exec(sql.c_str());
7999 sql = "DELETE FROM movielinktvshow WHERE NOT EXISTS (SELECT 1 FROM movie WHERE movie.idMovie = movielinktvshow.idMovie)";
8000 m_pDS->exec(sql.c_str());
8003 if (!musicVideoIDs.empty())
8005 std::string musicVideosToDelete;
8006 for (std::vector<int>::const_iterator it = musicVideoIDs.begin(); it != musicVideoIDs.end(); ++it)
8007 musicVideosToDelete += StringUtils::Format("%i,", *it);
8008 musicVideosToDelete = "(" + StringUtils::TrimRight(musicVideosToDelete, ",") + ")";
8010 CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8011 sql = "DELETE FROM musicvideo WHERE idMVideo IN " + musicVideosToDelete;
8012 m_pDS->exec(sql.c_str());
8014 CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8015 sql = "DELETE FROM artistlinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8016 m_pDS->exec(sql.c_str());
8018 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8019 sql = "DELETE FROM directorlinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8020 m_pDS->exec(sql.c_str());
8022 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8023 sql = "DELETE FROM genrelinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8024 m_pDS->exec(sql.c_str());
8026 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8027 sql = "DELETE FROM studiolinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8028 m_pDS->exec(sql.c_str());
8031 CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8032 sql = StringUtils::Format("DELETE FROM path "
8033 "WHERE (strContent IS NULL OR strContent = '') "
8034 "AND (strSettings IS NULL OR strSettings = '') "
8035 "AND (strHash IS NULL OR strHash = '') "
8036 "AND (exclude IS NULL OR exclude != 1) "
8037 "AND NOT EXISTS (SELECT 1 FROM files WHERE files.idPath = path.idPath) "
8038 "AND NOT EXISTS (SELECT 1 FROM tvshowlinkpath WHERE tvshowlinkpath.idPath = path.idPath) "
8039 "AND NOT EXISTS (SELECT 1 FROM movie WHERE movie.c%02d = path.idPath) "
8040 "AND NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.c%02d = path.idPath) "
8041 "AND NOT EXISTS (SELECT 1 FROM episode WHERE episode.c%02d = path.idPath) "
8042 "AND NOT EXISTS (SELECT 1 FROM musicvideo WHERE musicvideo.c%02d = path.idPath)"
8043 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8044 m_pDS->exec(sql.c_str());
8046 CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8047 sql = "DELETE FROM genre "
8048 "WHERE NOT EXISTS (SELECT 1 FROM genrelinkmovie WHERE genrelinkmovie.idGenre = genre.idGenre) "
8049 "AND NOT EXISTS (SELECT 1 FROM genrelinktvshow WHERE genrelinktvshow.idGenre = genre.idGenre) "
8050 "AND NOT EXISTS (SELECT 1 FROM genrelinkmusicvideo WHERE genrelinkmusicvideo.idGenre = genre.idGenre)";
8051 m_pDS->exec(sql.c_str());
8053 CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8054 sql = "DELETE FROM country WHERE NOT EXISTS (SELECT 1 FROM countrylinkmovie WHERE countrylinkmovie.idCountry = country.idCountry)";
8055 m_pDS->exec(sql.c_str());
8057 CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8058 sql = "DELETE FROM actors "
8059 "WHERE NOT EXISTS (SELECT 1 FROM actorlinkmovie WHERE actorlinkmovie.idActor = actors.idActor) "
8060 "AND NOT EXISTS (SELECT 1 FROM directorlinkmovie WHERE directorlinkmovie.idDirector = actors.idActor) "
8061 "AND NOT EXISTS (SELECT 1 FROM writerlinkmovie WHERE writerlinkmovie.idWriter = actors.idActor) "
8062 "AND NOT EXISTS (SELECT 1 FROM actorlinktvshow WHERE actorlinktvshow.idActor = actors.idActor) "
8063 "AND NOT EXISTS (SELECT 1 FROM actorlinkepisode WHERE actorlinkepisode.idActor = actors.idActor) "
8064 "AND NOT EXISTS (SELECT 1 FROM directorlinktvshow WHERE directorlinktvshow.idDirector = actors.idActor) "
8065 "AND NOT EXISTS (SELECT 1 FROM directorlinkepisode WHERE directorlinkepisode.idDirector = actors.idActor) "
8066 "AND NOT EXISTS (SELECT 1 FROM writerlinkepisode WHERE writerlinkepisode.idWriter = actors.idActor) "
8067 "AND NOT EXISTS (SELECT 1 FROM artistlinkmusicvideo WHERE artistlinkmusicvideo.idArtist = actors.idActor) "
8068 "AND NOT EXISTS (SELECT 1 FROM directorlinkmusicvideo WHERE directorlinkmusicvideo.idDirector = actors.idActor)";
8069 m_pDS->exec(sql.c_str());
8071 CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8072 sql = "DELETE FROM studio "
8073 "WHERE NOT EXISTS (SELECT 1 FROM studiolinkmovie WHERE studiolinkmovie.idStudio = studio.idStudio) "
8074 "AND NOT EXISTS (SELECT 1 FROM studiolinkmusicvideo WHERE studiolinkmusicvideo.idStudio = studio.idStudio) "
8075 "AND NOT EXISTS (SELECT 1 FROM studiolinktvshow WHERE studiolinktvshow.idStudio = studio.idStudio)";
8076 m_pDS->exec(sql.c_str());
8078 CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8079 sql = "DELETE FROM sets WHERE NOT EXISTS (SELECT 1 FROM movie WHERE movie.idSet = sets.idSet)";
8080 m_pDS->exec(sql.c_str());
8082 CommitTransaction();
8085 handle->SetTitle(g_localizeStrings.Get(331));
8089 CUtil::DeleteVideoDatabaseDirectoryCache();
8091 time = XbmcThreads::SystemClockMillis() - time;
8092 CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8094 for (std::vector<int>::const_iterator it = movieIDs.begin(); it != movieIDs.end(); ++it)
8095 AnnounceRemove("movie", *it);
8097 for (std::vector<int>::const_iterator it = episodeIDs.begin(); it != episodeIDs.end(); ++it)
8098 AnnounceRemove("episode", *it);
8100 for (std::vector<int>::const_iterator it = tvshowIDs.begin(); it != tvshowIDs.end(); ++it)
8101 AnnounceRemove("tvshow", *it);
8103 for (std::vector<int>::const_iterator it = musicVideoIDs.begin(); it != musicVideoIDs.end(); ++it)
8104 AnnounceRemove("musicvideo", *it);
8108 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8109 RollbackTransaction();
8114 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8117 std::vector<int> CVideoDatabase::CleanMediaType(const std::string &mediaType, const std::string &cleanableFileIDs,
8118 std::map<int, bool> &pathsDeleteDecisions, std::string &deletedFileIDs, bool silent)
8120 std::vector<int> cleanedIDs;
8121 if (mediaType.empty() || cleanableFileIDs.empty())
8124 std::string table = mediaType;
8125 std::string idField;
8126 std::string parentPathIdField;
8127 bool isEpisode = false;
8128 if (mediaType == "movie")
8130 idField = "idMovie";
8131 parentPathIdField = StringUtils::Format("%s.c%02d", table.c_str(), VIDEODB_ID_PARENTPATHID);
8133 else if (mediaType == "episode")
8135 idField = "idEpisode";
8136 parentPathIdField = StringUtils::Format("tvshow.c%02d", VIDEODB_ID_TV_PARENTPATHID);
8139 else if (mediaType == "musicvideo")
8141 idField = "idMVideo";
8142 parentPathIdField = StringUtils::Format("%s.c%02d", table.c_str(), VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
8147 // now grab them media items
8148 std::string sql = PrepareSQL("SELECT %s.%s, %s.idFile, %s, path.idPath, parentPath.strPath, parentPath.useFolderNames FROM %s "
8149 "JOIN files ON files.idFile = %s.idFile "
8150 "JOIN path ON path.idPath = files.idPath ",
8151 table.c_str(), idField.c_str(), table.c_str(), parentPathIdField.c_str(), table.c_str(),
8155 sql += "JOIN tvshow ON tvshow.idShow = episode.idShow ";
8157 sql += PrepareSQL("JOIN path as parentPath ON parentPath.idPath = %s "
8158 "WHERE %s.idFile IN (%s)",
8159 parentPathIdField.c_str(),
8160 table.c_str(), cleanableFileIDs.c_str());
8162 // map of parent path ID to boolean pair (if not exists and user choice)
8163 std::map<int, std::pair<bool, bool> > parentPathsDeleteDecisions;
8164 m_pDS->query(sql.c_str());
8165 while (!m_pDS->eof())
8167 int parentPathID = m_pDS->fv(2).get_asInt();
8168 std::map<int, std::pair<bool, bool> >::const_iterator parentPathsDeleteDecision = parentPathsDeleteDecisions.find(parentPathID);
8170 if (parentPathsDeleteDecision == parentPathsDeleteDecisions.end())
8172 std::string parentPath = m_pDS->fv(4).get_asString();
8173 bool parentPathNotExists = !CDirectory::Exists(parentPath, false);
8174 // if the parent path exists, the file will be deleted without asking
8175 // if the parent path doesn't exist, ask the user whether to remove all items it contained
8176 if (parentPathNotExists)
8178 // in silent mode assume that the files are just temporarily missing
8183 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
8184 if (pDialog != NULL)
8186 CURL parentUrl(parentPath);
8187 pDialog->SetHeading(15012);
8188 pDialog->SetText(StringUtils::Format(g_localizeStrings.Get(15013), parentUrl.GetWithoutUserDetails().c_str()));
8189 pDialog->SetChoice(0, 15015);
8190 pDialog->SetChoice(1, 15014);
8192 //send message and wait for user input
8193 ThreadMessage tMsg = { TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, (unsigned int)g_windowManager.GetActiveWindow() };
8194 CApplicationMessenger::Get().SendMessage(tMsg, true);
8196 del = !pDialog->IsConfirmed();
8201 parentPathsDeleteDecisions.insert(make_pair(parentPathID, make_pair(parentPathNotExists, del)));
8203 // the only reason not to delete the file is if the parent path doesn't
8204 // exist and the user decided to delete all the items it contained
8205 else if (parentPathsDeleteDecision->second.first &&
8206 !parentPathsDeleteDecision->second.second)
8209 if (m_pDS->fv(5).get_asBool())
8210 pathsDeleteDecisions.insert(make_pair(m_pDS->fv(3).get_asInt(), del));
8214 deletedFileIDs += m_pDS->fv(1).get_asString() + ",";
8215 cleanedIDs.push_back(m_pDS->fv(0).get_asInt());
8225 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8228 CFileItemList items;
8229 GetTvShowsByWhere("videodb://tvshows/titles/", "", items);
8230 CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8231 CDirectory::Create(showPath);
8232 for (int i = 0; i < items.Size(); i++)
8234 // create a folder in this directory
8235 CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8236 CStdString TVFolder = URIUtils::AddFileToFolder(showPath, showName);
8237 if (CDirectory::Create(TVFolder))
8238 { // right - grab the episodes and dump them as well
8239 CFileItemList episodes;
8240 Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8241 GetEpisodesByWhere("videodb://tvshows/titles/", filter, episodes);
8242 for (int i = 0; i < episodes.Size(); i++)
8244 CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8245 CStdString episode = StringUtils::Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8247 CStdString episodePath = URIUtils::AddFileToFolder(TVFolder, episode);
8249 if (file.OpenForWrite(episodePath))
8256 GetMoviesByWhere("videodb://movies/titles/", "", items);
8257 CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8258 CDirectory::Create(moviePath);
8259 for (int i = 0; i < items.Size(); i++)
8261 CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8262 CStdString movie = StringUtils::Format("%s.avi", tag->m_strTitle.c_str());
8264 if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8269 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8271 CGUIDialogProgress *progress=NULL;
8274 if (NULL == m_pDB.get()) return;
8275 if (NULL == m_pDS.get()) return;
8276 if (NULL == m_pDS2.get()) return;
8278 // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8279 auto_ptr<Dataset> pDS;
8280 pDS.reset(m_pDB->CreateDataset());
8281 if (NULL == pDS.get()) return;
8283 auto_ptr<Dataset> pDS2;
8284 pDS2.reset(m_pDB->CreateDataset());
8285 if (NULL == pDS2.get()) return;
8287 // if we're exporting to a single folder, we export thumbs as well
8288 CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8289 CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8290 CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8291 CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8292 CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8293 CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8299 CDirectory::Remove(exportRoot);
8300 CDirectory::Create(exportRoot);
8301 CDirectory::Create(actorsDir);
8302 CDirectory::Create(moviesDir);
8303 CDirectory::Create(musicvideosDir);
8304 CDirectory::Create(tvshowsDir);
8307 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8309 CStdString sql = "select * from movieview";
8311 m_pDS->query(sql.c_str());
8315 progress->SetHeading(647);
8316 progress->SetLine(0, 650);
8317 progress->SetLine(1, "");
8318 progress->SetLine(2, "");
8319 progress->SetPercentage(0);
8320 progress->StartModal();
8321 progress->ShowProgressBar(true);
8324 int total = m_pDS->num_rows();
8327 // create our xml document
8328 CXBMCTinyXML xmlDoc;
8329 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8330 xmlDoc.InsertEndChild(decl);
8331 TiXmlNode *pMain = NULL;
8336 TiXmlElement xmlMainElement("videodb");
8337 pMain = xmlDoc.InsertEndChild(xmlMainElement);
8338 XMLUtils::SetInt(pMain,"version", GetExportVersion());
8341 while (!m_pDS->eof())
8343 CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8344 // strip paths to make them relative
8345 if (StringUtils::StartsWith(movie.m_strTrailer, movie.m_strPath))
8346 movie.m_strTrailer = movie.m_strTrailer.substr(movie.m_strPath.size());
8347 map<string, string> artwork;
8348 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8350 TiXmlElement additionalNode("art");
8351 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8352 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8353 movie.Save(pMain, "movie", true, &additionalNode);
8356 movie.Save(pMain, "movie", !singleFiles);
8358 // reset old skip state
8363 progress->SetLine(1, movie.m_strTitle);
8364 progress->SetPercentage(current * 100 / total);
8365 progress->Progress();
8366 if (progress->IsCanceled())
8374 CFileItem item(movie.m_strFileNameAndPath,false);
8375 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8377 if (!item.Exists(false))
8379 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8384 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8386 if (item.IsOpticalMediaFile())
8388 nfoFile = URIUtils::AddFileToFolder(
8389 URIUtils::GetParentPath(nfoFile),
8390 URIUtils::GetFileName(nfoFile));
8393 if (overwrite || !CFile::Exists(nfoFile, false))
8395 if(!xmlDoc.SaveFile(nfoFile))
8397 CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8398 bSkip = ExportSkipEntry(nfoFile);
8415 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8416 xmlDoc.InsertEndChild(decl);
8419 if (images && !bSkip)
8423 CStdString strFileName(movie.m_strTitle);
8424 if (movie.m_iYear > 0)
8425 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8426 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8428 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8430 CStdString savedThumb = item.GetLocalArt(i->first, false);
8431 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8434 ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8441 // find all musicvideos
8442 sql = "select * from musicvideoview";
8444 m_pDS->query(sql.c_str());
8446 total = m_pDS->num_rows();
8449 while (!m_pDS->eof())
8451 CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8452 map<string, string> artwork;
8453 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8455 TiXmlElement additionalNode("art");
8456 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8457 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8458 movie.Save(pMain, "musicvideo", true, &additionalNode);
8461 movie.Save(pMain, "musicvideo", !singleFiles);
8463 // reset old skip state
8468 progress->SetLine(1, movie.m_strTitle);
8469 progress->SetPercentage(current * 100 / total);
8470 progress->Progress();
8471 if (progress->IsCanceled())
8479 CFileItem item(movie.m_strFileNameAndPath,false);
8480 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8482 if (!item.Exists(false))
8484 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8489 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8491 if (overwrite || !CFile::Exists(nfoFile, false))
8493 if(!xmlDoc.SaveFile(nfoFile))
8495 CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8496 bSkip = ExportSkipEntry(nfoFile);
8513 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8514 xmlDoc.InsertEndChild(decl);
8516 if (images && !bSkip)
8520 CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8521 if (movie.m_iYear > 0)
8522 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8523 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8525 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8527 CStdString savedThumb = item.GetLocalArt(i->first, false);
8528 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8536 // repeat for all tvshows
8537 sql = "SELECT * FROM tvshowview";
8538 m_pDS->query(sql.c_str());
8540 total = m_pDS->num_rows();
8543 while (!m_pDS->eof())
8545 CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8547 map<int, map<string, string> > seasonArt;
8548 GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8550 map<string, string> artwork;
8551 if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8553 TiXmlElement additionalNode("art");
8554 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8555 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8556 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8558 TiXmlElement seasonNode("season");
8559 seasonNode.SetAttribute("num", i->first);
8560 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8561 XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8562 additionalNode.InsertEndChild(seasonNode);
8564 tvshow.Save(pMain, "tvshow", true, &additionalNode);
8567 tvshow.Save(pMain, "tvshow", !singleFiles);
8569 // reset old skip state
8574 progress->SetLine(1, tvshow.m_strTitle);
8575 progress->SetPercentage(current * 100 / total);
8576 progress->Progress();
8577 if (progress->IsCanceled())
8585 // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8586 if (URIUtils::IsMultiPath(tvshow.m_strPath))
8587 tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8589 CFileItem item(tvshow.m_strPath, true);
8590 if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8592 if (!item.Exists(false))
8594 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8599 CStdString nfoFile = URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo");
8601 if (overwrite || !CFile::Exists(nfoFile, false))
8603 if(!xmlDoc.SaveFile(nfoFile))
8605 CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8606 bSkip = ExportSkipEntry(nfoFile);
8623 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8624 xmlDoc.InsertEndChild(decl);
8626 if (images && !bSkip)
8629 item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8631 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8633 CStdString savedThumb = item.GetLocalArt(i->first, true);
8634 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8638 ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8640 // export season thumbs
8641 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8645 seasonThumb = "season-all";
8646 else if (i->first == 0)
8647 seasonThumb = "season-specials";
8649 seasonThumb = StringUtils::Format("season%02i", i->first);
8650 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8652 CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8653 if (!i->second.empty())
8654 CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8659 // now save the episodes from this show
8660 sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8661 pDS->query(sql.c_str());
8662 CStdString showDir(item.GetPath());
8666 CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8667 map<string, string> artwork;
8668 if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8670 TiXmlElement additionalNode("art");
8671 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8672 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8673 episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8675 else if (!singleFiles)
8676 episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8678 episode.Save(pMain, "episodedetails", !singleFiles);
8680 // multi-episode files need dumping to the same XML
8681 while (singleFiles && !pDS->eof() &&
8682 episode.m_iFileId == pDS->fv("idFile").get_asInt())
8684 episode = GetDetailsForEpisode(pDS, true);
8685 episode.Save(pMain, "episodedetails", !singleFiles);
8689 // reset old skip state
8692 CFileItem item(episode.m_strFileNameAndPath, false);
8693 if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8695 if (!item.Exists(false))
8697 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8702 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8704 if (overwrite || !CFile::Exists(nfoFile, false))
8706 if(!xmlDoc.SaveFile(nfoFile))
8708 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8709 bSkip = ExportSkipEntry(nfoFile);
8726 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8727 xmlDoc.InsertEndChild(decl);
8730 if (images && !bSkip)
8734 CStdString epName = StringUtils::Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8735 item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8737 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8739 CStdString savedThumb = item.GetLocalArt(i->first, false);
8740 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8743 ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8752 if (singleFiles && progress)
8754 progress->SetPercentage(100);
8755 progress->Progress();
8760 // now dump path info
8761 set<CStdString> paths;
8763 TiXmlElement xmlPathElement("paths");
8764 TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8765 for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8767 bool foundDirectly = false;
8768 SScanSettings settings;
8769 ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8770 if (info && foundDirectly)
8772 TiXmlElement xmlPathElement2("path");
8773 TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8774 XMLUtils::SetString(pPath,"url", *iter);
8775 XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8776 XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8777 XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8778 XMLUtils::SetString(pPath,"scraperpath", info->ID());
8781 xmlDoc.SaveFile(xmlFile);
8786 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8793 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8795 CStdString strPath(strDir);
8798 strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8799 if (!CDirectory::Exists(strPath))
8801 CDirectory::Create(strPath);
8802 CFile::SetHidden(strPath, true);
8806 for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8809 item.SetLabel(iter->strName);
8810 if (!iter->thumb.empty())
8812 CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8813 CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8818 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8820 CStdString strParent;
8821 URIUtils::GetParentPath(nfoFile,strParent);
8822 CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8824 bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8827 CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8829 CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8834 void CVideoDatabase::ImportFromXML(const CStdString &path)
8836 CGUIDialogProgress *progress=NULL;
8839 if (NULL == m_pDB.get()) return;
8840 if (NULL == m_pDS.get()) return;
8842 CXBMCTinyXML xmlDoc;
8843 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
8846 TiXmlElement *root = xmlDoc.RootElement();
8849 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8852 progress->SetHeading(648);
8853 progress->SetLine(0, 649);
8854 progress->SetLine(1, 330);
8855 progress->SetLine(2, "");
8856 progress->SetPercentage(0);
8857 progress->StartModal();
8858 progress->ShowProgressBar(true);
8862 XMLUtils::GetInt(root, "version", iVersion);
8864 CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
8866 TiXmlElement *movie = root->FirstChildElement();
8869 // first count the number of items...
8872 if (strnicmp(movie->Value(), "movie", 5)==0 ||
8873 strnicmp(movie->Value(), "tvshow", 6)==0 ||
8874 strnicmp(movie->Value(), "musicvideo",10)==0 )
8876 movie = movie->NextSiblingElement();
8879 CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
8880 CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
8881 CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
8882 CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
8883 CVideoInfoScanner scanner;
8884 // add paths first (so we have scraper settings available)
8885 TiXmlElement *path = root->FirstChildElement("paths");
8886 path = path->FirstChildElement();
8890 if (XMLUtils::GetString(path,"url",strPath) && !strPath.empty())
8894 if (XMLUtils::GetString(path,"content", content) && !content.empty())
8895 { // check the scraper exists, if so store the path
8898 XMLUtils::GetString(path,"scraperpath",id);
8899 if (CAddonMgr::Get().GetAddon(id, addon))
8901 SScanSettings settings;
8902 ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
8903 // FIXME: scraper settings are not exported?
8904 scraper->SetPathSettings(TranslateContent(content), "");
8905 XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
8906 XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
8907 SetScraperForPath(strPath,scraper,settings);
8910 path = path->NextSiblingElement();
8912 movie = root->FirstChildElement();
8916 if (strnicmp(movie->Value(), "movie", 5) == 0)
8919 CFileItem item(info);
8920 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
8921 CStdString filename = info.m_strTitle;
8922 if (info.m_iYear > 0)
8923 filename += StringUtils::Format("_%i", info.m_iYear);
8924 CFileItem artItem(item);
8925 artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
8926 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8927 item.SetArt(artItem.GetArt());
8928 scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
8931 else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
8934 CFileItem item(info);
8935 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
8936 CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
8937 if (info.m_iYear > 0)
8938 filename += StringUtils::Format("_%i", info.m_iYear);
8939 CFileItem artItem(item);
8940 artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
8941 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8942 item.SetArt(artItem.GetArt());
8943 scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
8946 else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
8948 // load the TV show in. NOTE: This deletes all episodes under the TV Show, which may not be
8949 // what we desire. It may make better sense to only delete (or even better, update) the show information
8951 URIUtils::AddSlashAtEnd(info.m_strPath);
8952 DeleteTvShow(info.m_strPath);
8953 CFileItem showItem(info);
8954 bool useFolders = info.m_basePath.empty() ? LookupByFolders(showItem.GetPath(), true) : false;
8955 CFileItem artItem(showItem);
8956 CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
8957 artItem.SetPath(artPath);
8958 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8959 showItem.SetArt(artItem.GetArt());
8960 int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
8962 map<int, map<string, string> > seasonArt;
8963 artItem.GetVideoInfoTag()->m_strPath = artPath;
8964 scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
8965 for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8967 int seasonID = AddSeason(showID, i->first);
8968 SetArtForItem(seasonID, "season", i->second);
8971 // now load the episodes
8972 TiXmlElement *episode = movie->FirstChildElement("episodedetails");
8975 // no need to delete the episode info, due to the above deletion
8978 CFileItem item(info);
8979 CStdString filename = StringUtils::Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
8980 CFileItem artItem(item);
8981 artItem.SetPath(GetSafeFile(artPath, filename));
8982 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8983 item.SetArt(artItem.GetArt());
8984 scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
8985 episode = episode->NextSiblingElement("episodedetails");
8988 movie = movie->NextSiblingElement();
8989 if (progress && total)
8991 progress->SetPercentage(current * 100 / total);
8992 progress->SetLine(2, info.m_strTitle);
8993 progress->Progress();
8994 if (progress->IsCanceled())
9004 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
9010 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
9012 if (!node) return false;
9013 const TiXmlNode *art = node->FirstChild();
9014 while (art && art->FirstChild())
9016 artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
9017 art = art->NextSibling();
9019 return !artwork.empty();
9022 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
9024 if (URIUtils::IsStack(strFileName) ||
9025 URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
9026 strDest = strFileName;
9028 strDest = URIUtils::AddFileToFolder(strPath, strFileName);
9031 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
9033 if (URIUtils::IsStack(strFileNameAndPath) || StringUtils::StartsWithNoCase(strFileNameAndPath, "rar://") || StringUtils::StartsWithNoCase(strFileNameAndPath, "zip://"))
9035 URIUtils::GetParentPath(strFileNameAndPath,strPath);
9036 strFileName = strFileNameAndPath;
9038 else if (URIUtils::IsPlugin(strFileNameAndPath))
9040 CURL url(strFileNameAndPath);
9041 strPath = url.GetWithoutFilename();
9042 strFileName = strFileNameAndPath;
9045 URIUtils::Split(strFileNameAndPath,strPath, strFileName);
9048 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
9050 SScanSettings settings;
9052 ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
9053 SetPathHash(strPath,"");
9056 if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
9058 if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
9060 CStdString strParent;
9061 URIUtils::GetParentPath(strPath,strParent);
9062 SetPathHash(strParent,"");
9067 bool CVideoDatabase::CommitTransaction()
9069 if (CDatabase::CommitTransaction())
9070 { // number of items in the db has likely changed, so recalculate
9071 g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
9072 g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
9073 g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
9079 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, int dbField, const std::string &strValue)
9084 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9087 string strTable, strField;
9088 if (type == VIDEODB_CONTENT_MOVIES)
9091 strField = "idMovie";
9093 else if (type == VIDEODB_CONTENT_TVSHOWS)
9095 strTable = "tvshow";
9096 strField = "idShow";
9098 else if (type == VIDEODB_CONTENT_EPISODES)
9100 strTable = "episode";
9101 strField = "idEpisode";
9103 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
9105 strTable = "musicvideo";
9106 strField = "idMVideo";
9109 if (strTable.empty())
9112 return SetSingleValue(strTable, StringUtils::Format("c%02u", dbField), strValue, strField, dbId);
9116 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
9121 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, Field dbField, const std::string &strValue)
9123 MediaType mediaType = DatabaseUtils::MediaTypeFromVideoContentType(type);
9124 if (mediaType == MediaTypeNone)
9127 int dbFieldIndex = DatabaseUtils::GetField(dbField, mediaType);
9128 if (dbFieldIndex < 0)
9131 return SetSingleValue(type, dbId, dbFieldIndex, strValue);
9134 bool CVideoDatabase::SetSingleValue(const std::string &table, const std::string &fieldName, const std::string &strValue,
9135 const std::string &conditionName /* = "" */, int conditionValue /* = -1 */)
9137 if (table.empty() || fieldName.empty())
9143 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9146 sql = PrepareSQL("UPDATE %s SET %s='%s'", table.c_str(), fieldName.c_str(), strValue.c_str());
9147 if (!conditionName.empty())
9148 sql += PrepareSQL(" WHERE %s=%u", conditionName.c_str(), conditionValue);
9149 if (m_pDS->exec(sql.c_str()) == 0)
9154 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, sql.c_str());
9159 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
9161 CStdString safeThumb(name);
9162 StringUtils::Replace(safeThumb, ' ', '_');
9163 return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
9166 void CVideoDatabase::AnnounceRemove(std::string content, int id)
9169 data["type"] = content;
9171 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9174 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9177 data["type"] = content;
9179 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9182 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9184 CStdString path(strPath);
9186 if(URIUtils::IsMultiPath(path))
9188 vector<CStdString> paths;
9189 CMultiPathDirectory::GetPaths(path, paths);
9191 for(unsigned i=0;i<paths.size();i++)
9192 GetItemsForPath(content, paths[i], items);
9194 return items.Size() > 0;
9197 int pathID = GetPathId(path);
9201 if (content == "movies")
9203 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9204 GetMoviesByWhere("videodb://movies/titles/", filter, items);
9206 else if (content == "episodes")
9208 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9209 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
9211 else if (content == "tvshows")
9213 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9214 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
9216 else if (content == "musicvideos")
9218 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9219 GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
9221 for (int i = 0; i < items.Size(); i++)
9222 items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9223 return items.Size() > 0;
9226 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9228 if (!videoUrl.IsValid())
9231 std::string type = videoUrl.GetType();
9232 std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9233 const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9234 CUrlOptions::UrlOptions::const_iterator option;
9236 if (type == "movies")
9238 option = options.find("genreid");
9239 if (option != options.end())
9241 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9242 filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9245 option = options.find("genre");
9246 if (option != options.end())
9248 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9249 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9252 option = options.find("countryid");
9253 if (option != options.end())
9255 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9256 filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9259 option = options.find("country");
9260 if (option != options.end())
9262 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9263 filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9266 option = options.find("studioid");
9267 if (option != options.end())
9269 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9270 filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9273 option = options.find("studio");
9274 if (option != options.end())
9276 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9277 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9280 option = options.find("directorid");
9281 if (option != options.end())
9283 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9284 filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9287 option = options.find("director");
9288 if (option != options.end())
9290 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9291 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9294 option = options.find("year");
9295 if (option != options.end())
9296 filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9298 option = options.find("actorid");
9299 if (option != options.end())
9301 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9302 filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9305 option = options.find("actor");
9306 if (option != options.end())
9308 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9309 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9312 option = options.find("setid");
9313 if (option != options.end())
9314 filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9316 option = options.find("set");
9317 if (option != options.end())
9319 filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9320 filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9323 option = options.find("tagid");
9324 if (option != options.end())
9326 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9327 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9330 option = options.find("tag");
9331 if (option != options.end())
9333 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9334 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9337 else if (type == "tvshows")
9339 if (itemType == "tvshows")
9341 option = options.find("genreid");
9342 if (option != options.end())
9344 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9345 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9348 option = options.find("genre");
9349 if (option != options.end())
9351 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9352 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9355 option = options.find("studioid");
9356 if (option != options.end())
9358 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9359 filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9362 option = options.find("studio");
9363 if (option != options.end())
9365 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9366 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9369 option = options.find("directorid");
9370 if (option != options.end())
9372 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9373 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9376 option = options.find("year");
9377 if (option != options.end())
9378 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9380 option = options.find("actorid");
9381 if (option != options.end())
9383 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9384 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9387 option = options.find("actor");
9388 if (option != options.end())
9390 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9391 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9394 option = options.find("tagid");
9395 if (option != options.end())
9397 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9398 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9401 option = options.find("tag");
9402 if (option != options.end())
9404 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9405 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9408 else if (itemType == "seasons")
9410 option = options.find("genreid");
9411 if (option != options.end())
9413 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9414 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9417 option = options.find("directorid");
9418 if (option != options.end())
9420 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9421 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9424 option = options.find("year");
9425 if (option != options.end())
9426 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9428 option = options.find("actorid");
9429 if (option != options.end())
9431 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9432 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9435 else if (itemType == "episodes")
9438 option = options.find("tvshowid");
9439 if (option != options.end())
9440 idShow = (int)option->second.asInteger();
9443 option = options.find("season");
9444 if (option != options.end())
9445 season = (int)option->second.asInteger();
9447 CStdString strIn = PrepareSQL("= %i", idShow);
9448 GetStackedTvShowList(idShow, strIn);
9452 bool condition = false;
9454 option = options.find("genreid");
9455 if (option != options.end())
9458 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9459 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9462 option = options.find("genre");
9463 if (option != options.end())
9466 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9467 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9470 option = options.find("directorid");
9471 if (option != options.end())
9474 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9475 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9478 option = options.find("director");
9479 if (option != options.end())
9482 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9483 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9486 option = options.find("year");
9487 if (option != options.end())
9490 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9493 option = options.find("actorid");
9494 if (option != options.end())
9497 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9498 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9501 option = options.find("actor");
9502 if (option != options.end())
9505 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9506 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9510 filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9514 if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9515 filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9517 filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9518 VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9523 option = options.find("year");
9524 if (option != options.end())
9525 filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9527 option = options.find("directorid");
9528 if (option != options.end())
9530 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9531 filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9534 option = options.find("director");
9535 if (option != options.end())
9537 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9538 filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9543 else if (type == "musicvideos")
9545 option = options.find("genreid");
9546 if (option != options.end())
9548 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9549 filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9552 option = options.find("genre");
9553 if (option != options.end())
9555 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9556 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9559 option = options.find("studioid");
9560 if (option != options.end())
9562 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9563 filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9566 option = options.find("studio");
9567 if (option != options.end())
9569 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9570 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9573 option = options.find("directorid");
9574 if (option != options.end())
9576 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9577 filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9580 option = options.find("director");
9581 if (option != options.end())
9583 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9584 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9587 option = options.find("year");
9588 if (option != options.end())
9589 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9591 option = options.find("artistid");
9592 if (option != options.end())
9594 if (itemType != "albums")
9595 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9596 filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9599 option = options.find("artist");
9600 if (option != options.end())
9602 if (itemType != "albums")
9604 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9605 filter.AppendJoin(PrepareSQL("join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9607 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9610 option = options.find("albumid");
9611 if (option != options.end())
9612 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = (select c%02d from musicvideo where idMVideo = %i)", VIDEODB_ID_MUSICVIDEO_ALBUM, VIDEODB_ID_MUSICVIDEO_ALBUM, (int)option->second.asInteger()));
9614 option = options.find("tagid");
9615 if (option != options.end())
9617 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9618 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9621 option = options.find("tag");
9622 if (option != options.end())
9624 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9625 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9631 option = options.find("xsp");
9632 if (option != options.end())
9635 if (!xsp.LoadFromJson(option->second.asString()))
9638 // check if the filter playlist matches the item type
9639 if (xsp.GetType() == itemType ||
9640 (xsp.GetGroup() == itemType && !xsp.IsGroupMixed()) ||
9641 // handle episode listings with videodb://tvshows/titles/ which get the rest
9642 // of the path (season and episodeid) appended later
9643 (xsp.GetType() == "episodes" && itemType == "tvshows"))
9645 std::set<CStdString> playlists;
9646 filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9648 if (xsp.GetLimit() > 0)
9649 sorting.limitEnd = xsp.GetLimit();
9650 if (xsp.GetOrder() != SortByNone)
9651 sorting.sortBy = xsp.GetOrder();
9652 if (xsp.GetOrderDirection() != SortOrderNone)
9653 sorting.sortOrder = xsp.GetOrderDirection();
9654 if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
9655 sorting.sortAttributes = SortAttributeIgnoreArticle;
9659 option = options.find("filter");
9660 if (option != options.end())
9662 CSmartPlaylist xspFilter;
9663 if (!xspFilter.LoadFromJson(option->second.asString()))
9666 // check if the filter playlist matches the item type
9667 if (xspFilter.GetType() == itemType)
9669 std::set<CStdString> playlists;
9670 filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9672 // remove the filter if it doesn't match the item type
9674 videoUrl.RemoveOption("filter");