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 strSQL=PrepareSQL("delete from writerlinkmovie where idMovie=%i", idMovie);
2869 m_pDS->exec(strSQL.c_str());
2871 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2873 // keep the movie table entry, linking to tv shows, and bookmarks
2874 // so we can update the data in place
2875 // the ancilliary tables are still purged
2878 ClearBookMarksOfFile(strFilenameAndPath);
2880 strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2881 m_pDS->exec(strSQL.c_str());
2883 strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2884 m_pDS->exec(strSQL.c_str());
2886 CStdString strPath, strFileName;
2887 SplitPath(strFilenameAndPath,strPath,strFileName);
2888 InvalidatePathHash(strPath);
2891 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2893 AnnounceRemove("movie", idMovie);
2895 CommitTransaction();
2900 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2901 RollbackTransaction();
2905 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2911 GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2913 DeleteTvShow(path, bKeepId, idTvShow);
2916 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2920 if (NULL == m_pDB.get()) return ;
2921 if (NULL == m_pDS.get()) return ;
2924 idTvShow = GetTvShowId(strPath);
2931 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);
2932 m_pDS2->query(strSQL.c_str());
2933 while (!m_pDS2->eof())
2935 CStdString strFilenameAndPath;
2936 CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2937 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2938 ConstructPath(strFilenameAndPath, strPath, strFileName);
2939 DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2943 DeleteDetailsForTvShow(strPath, idTvShow);
2945 strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2946 m_pDS->exec(strSQL.c_str());
2948 // keep tvshow table and movielink table so we can update data in place
2951 strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2952 m_pDS->exec(strSQL.c_str());
2954 strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2955 m_pDS->exec(strSQL.c_str());
2957 strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2958 m_pDS->exec(strSQL.c_str());
2960 InvalidatePathHash(strPath);
2963 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2965 AnnounceRemove("tvshow", idTvShow);
2967 CommitTransaction();
2972 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2973 RollbackTransaction();
2977 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2983 GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2985 DeleteEpisode(path, idEpisode, bKeepId);
2988 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
2992 if (NULL == m_pDB.get()) return ;
2993 if (NULL == m_pDS.get()) return ;
2996 idEpisode = GetEpisodeId(strFilenameAndPath);
3003 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3005 AnnounceRemove("episode", idEpisode);
3008 strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
3009 m_pDS->exec(strSQL.c_str());
3011 strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
3012 m_pDS->exec(strSQL.c_str());
3014 strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
3015 m_pDS->exec(strSQL.c_str());
3017 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3019 // keep episode table entry and bookmarks so we can update the data in place
3020 // the ancilliary tables are still purged
3023 ClearBookMarksOfFile(strFilenameAndPath);
3025 strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
3026 m_pDS->exec(strSQL.c_str());
3032 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3036 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
3038 if (idMusicVideo < 0)
3042 GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
3044 DeleteMusicVideo(path, bKeepId, idMusicVideo);
3047 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
3051 if (NULL == m_pDB.get()) return ;
3052 if (NULL == m_pDS.get()) return ;
3055 idMVideo = GetMusicVideoId(strFilenameAndPath);
3063 strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
3064 m_pDS->exec(strSQL.c_str());
3066 strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
3067 m_pDS->exec(strSQL.c_str());
3069 strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
3070 m_pDS->exec(strSQL.c_str());
3072 strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
3073 m_pDS->exec(strSQL.c_str());
3075 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3077 // keep the music video table entry and bookmarks so we can update data in place
3078 // the ancilliary tables are still purged
3081 ClearBookMarksOfFile(strFilenameAndPath);
3083 strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
3084 m_pDS->exec(strSQL.c_str());
3086 CStdString strPath, strFileName;
3087 SplitPath(strFilenameAndPath,strPath,strFileName);
3088 InvalidatePathHash(strPath);
3091 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3093 AnnounceRemove("musicvideo", idMVideo);
3095 CommitTransaction();
3100 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3101 RollbackTransaction();
3105 void CVideoDatabase::DeleteStreamDetails(int idFile)
3107 m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
3110 void CVideoDatabase::DeleteSet(int idSet)
3114 if (NULL == m_pDB.get()) return ;
3115 if (NULL == m_pDS.get()) return ;
3118 strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
3119 m_pDS->exec(strSQL.c_str());
3120 strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3121 m_pDS->exec(strSQL.c_str());
3125 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3129 void CVideoDatabase::ClearMovieSet(int idMovie)
3131 SetMovieSet(idMovie, -1);
3134 void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
3137 ExecuteQuery(PrepareSQL("update movie set idSet = %i where idMovie = %i", idSet, idMovie));
3139 ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
3142 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3146 if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3150 if (mediaType == VIDEODB_CONTENT_MOVIES)
3152 else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3154 else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3155 type = "musicvideo";
3160 strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3161 m_pDS->exec(strSQL.c_str());
3165 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3169 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3171 GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3174 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3176 for (int i = min + 1; i < max; i++)
3178 switch (offsets[i].type)
3180 case VIDEODB_TYPE_STRING:
3181 *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3183 case VIDEODB_TYPE_INT:
3184 case VIDEODB_TYPE_COUNT:
3185 *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3187 case VIDEODB_TYPE_BOOL:
3188 *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3190 case VIDEODB_TYPE_FLOAT:
3191 *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3193 case VIDEODB_TYPE_STRINGARRAY:
3194 *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3196 case VIDEODB_TYPE_DATE:
3197 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3199 case VIDEODB_TYPE_DATETIME:
3200 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3206 DWORD movieTime = 0;
3209 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3211 CVideoInfoTag details;
3216 case VIDEODB_CONTENT_MOVIES:
3217 GetMovieInfo("", details, id);
3219 case VIDEODB_CONTENT_TVSHOWS:
3220 GetTvShowInfo("", details, id);
3222 case VIDEODB_CONTENT_EPISODES:
3223 GetEpisodeInfo("", details, id);
3225 case VIDEODB_CONTENT_MUSICVIDEOS:
3226 GetMusicVideoInfo("", details, id);
3235 bool CVideoDatabase::GetStreamDetails(CFileItem& item)
3237 // Note that this function (possibly) creates VideoInfoTags for items that don't have one yet!
3240 if (item.HasVideoInfoTag())
3241 fileId = item.GetVideoInfoTag()->m_iFileId;
3244 fileId = GetFileId(item);
3249 // Have a file id, get stream details if available (creates tag either way)
3250 item.GetVideoInfoTag()->m_iFileId = fileId;
3251 return GetStreamDetails(*item.GetVideoInfoTag());
3254 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3256 if (tag.m_iFileId < 0)
3259 bool retVal = false;
3261 CStreamDetails& details = tag.m_streamDetails;
3264 auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3267 CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3272 CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3275 case CStreamDetail::VIDEO:
3277 CStreamDetailVideo *p = new CStreamDetailVideo();
3278 p->m_strCodec = pDS->fv(2).get_asString();
3279 p->m_fAspect = pDS->fv(3).get_asFloat();
3280 p->m_iWidth = pDS->fv(4).get_asInt();
3281 p->m_iHeight = pDS->fv(5).get_asInt();
3282 p->m_iDuration = pDS->fv(10).get_asInt();
3283 p->m_strStereoMode = pDS->fv(11).get_asString();
3284 details.AddStream(p);
3288 case CStreamDetail::AUDIO:
3290 CStreamDetailAudio *p = new CStreamDetailAudio();
3291 p->m_strCodec = pDS->fv(6).get_asString();
3292 if (pDS->fv(7).get_isNull())
3293 p->m_iChannels = -1;
3295 p->m_iChannels = pDS->fv(7).get_asInt();
3296 p->m_strLanguage = pDS->fv(8).get_asString();
3297 details.AddStream(p);
3301 case CStreamDetail::SUBTITLE:
3303 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3304 p->m_strLanguage = pDS->fv(9).get_asString();
3305 details.AddStream(p);
3318 CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3320 details.DetermineBestStreams();
3322 if (details.GetVideoDuration() > 0)
3323 tag.m_duration = details.GetVideoDuration();
3328 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3330 if (tag.m_iFileId < 0)
3337 if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3339 CStackDirectory dir;
3340 CFileItemList fileList;
3341 dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3342 tag.m_resumePoint.Reset();
3343 for (int i = fileList.Size() - 1; i >= 0; i--)
3346 if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3348 tag.m_resumePoint = bookmark;
3349 tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3357 CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3358 m_pDS2->query( strSQL.c_str() );
3361 tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3362 tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3363 tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3364 tag.m_resumePoint.type = CBookmark::RESUME;
3372 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3378 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3380 return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3383 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3385 CVideoInfoTag details;
3390 DWORD time = XbmcThreads::SystemClockMillis();
3391 int idMovie = record->at(0).get_asInt();
3393 GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3395 details.m_iDbId = idMovie;
3396 details.m_type = "movie";
3398 details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3399 details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3400 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3401 details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3402 CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3403 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3404 details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3405 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3406 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3407 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3408 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3409 details.m_resumePoint.type = CBookmark::RESUME;
3411 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3415 GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3417 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3418 details.m_strPictureURL.Parse();
3421 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);
3422 m_pDS2->query(strSQL.c_str());
3423 while (!m_pDS2->eof())
3425 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3429 // create tvshowlink string
3431 GetLinksToTvShow(idMovie,links);
3432 for (unsigned int i=0;i<links.size();++i)
3434 CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3435 VIDEODB_ID_TV_TITLE,links[i]);
3436 m_pDS2->query(strSQL.c_str());
3438 details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3442 // get streamdetails
3443 GetStreamDetails(details);
3448 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3450 return GetDetailsForTvShow(pDS->get_sql_record(), getDetails, item);
3453 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3455 CVideoInfoTag details;
3460 DWORD time = XbmcThreads::SystemClockMillis();
3461 int idTvShow = record->at(0).get_asInt();
3463 GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3464 details.m_iDbId = idTvShow;
3465 details.m_type = "tvshow";
3466 details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3467 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3468 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3469 details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3470 details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3471 details.m_strShowPath = details.m_strPath;
3472 details.m_strShowTitle = details.m_strTitle;
3473 if (details.m_premiered.IsValid())
3474 details.m_iYear = details.m_premiered.GetYear();
3476 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3480 GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3483 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);
3484 m_pDS2->query(strSQL.c_str());
3485 while (!m_pDS2->eof())
3487 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3491 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3492 details.m_strPictureURL.Parse();
3497 item->m_dateTime = details.m_premiered;
3498 item->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
3499 item->SetProperty("totalepisodes", details.m_iEpisode);
3500 item->SetProperty("numepisodes", details.m_iEpisode); // will be changed later to reflect watchmode setting
3501 item->SetProperty("watchedepisodes", details.m_playCount);
3502 item->SetProperty("unwatchedepisodes", details.m_iEpisode - details.m_playCount);
3504 details.m_playCount = (details.m_iEpisode <= details.m_playCount) ? 1 : 0;
3509 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3511 return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3514 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3516 CVideoInfoTag details;
3521 DWORD time = XbmcThreads::SystemClockMillis();
3522 int idEpisode = record->at(0).get_asInt();
3524 GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3525 details.m_iDbId = idEpisode;
3526 details.m_type = "episode";
3527 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3528 details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3529 CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3530 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3531 details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3532 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3533 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3534 details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3535 details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3536 details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3537 details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3538 details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3539 details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3540 details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3542 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3543 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3544 details.m_resumePoint.type = CBookmark::RESUME;
3546 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3550 GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3551 GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3553 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3554 details.m_strPictureURL.Parse();
3555 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);
3556 m_pDS2->query(strSQL.c_str());
3558 details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3561 // get streamdetails
3562 GetStreamDetails(details);
3567 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3569 return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3572 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3574 CVideoInfoTag details;
3576 unsigned int time = XbmcThreads::SystemClockMillis();
3577 int idMVideo = record->at(0).get_asInt();
3579 GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3580 details.m_iDbId = idMVideo;
3581 details.m_type = "musicvideo";
3583 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3584 details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3585 CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3586 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3587 details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3588 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3589 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3590 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3591 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3592 details.m_resumePoint.type = CBookmark::RESUME;
3594 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3599 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);
3600 m_pDS2->query(strSQL.c_str());
3601 while (!m_pDS2->eof())
3603 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3608 details.m_strPictureURL.Parse();
3610 // get streamdetails
3611 GetStreamDetails(details);
3616 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3620 if (!m_pDB.get()) return;
3621 if (!m_pDS2.get()) return;
3623 CStdString sql = PrepareSQL("SELECT actors.strActor,"
3624 " actorlink%s.strRole,"
3625 " actorlink%s.iOrder,"
3630 " actorlink%s.idActor=actors.idActor"
3632 " art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3633 "WHERE actorlink%s.%s=%i "
3634 "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());
3635 m_pDS2->query(sql.c_str());
3636 while (!m_pDS2->eof())
3639 info.strName = m_pDS2->fv(0).get_asString();
3641 for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3643 if (i->strName == info.strName)
3651 info.strRole = m_pDS2->fv(1).get_asString();
3652 info.order = m_pDS2->fv(2).get_asInt();
3653 info.thumbUrl.ParseString(m_pDS2->fv(3).get_asString());
3654 info.thumb = m_pDS2->fv(4).get_asString();
3655 cast.push_back(info);
3663 CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3667 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3668 /// \retval Returns true if the settings exist, false otherwise.
3669 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3673 // obtain the FileID (if it exists)
3674 #ifdef NEW_VIDEODB_METHODS
3675 if (NULL == m_pDB.get()) return false;
3676 if (NULL == m_pDS.get()) return false;
3677 CStdString strPath, strFileName;
3678 URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3679 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());
3681 int idFile = GetFileId(strFilenameAndPath);
3682 if (idFile < 0) return false;
3683 if (NULL == m_pDB.get()) return false;
3684 if (NULL == m_pDS.get()) return false;
3685 // ok, now obtain the settings for this file
3686 CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3688 m_pDS->query( strSQL.c_str() );
3689 if (m_pDS->num_rows() > 0)
3690 { // get the video settings info
3691 settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3692 settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3693 settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3694 settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3695 settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3696 settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3697 settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3698 settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3699 settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3700 settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3701 settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3702 settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3703 settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3704 settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3705 settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3706 settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3707 settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3708 settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3709 settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3710 settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3711 settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3712 settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3713 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3714 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3715 settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3716 settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3717 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3718 settings.m_StereoMode = m_pDS->fv("StereoMode").get_asInt();
3719 settings.m_StereoInvert = m_pDS->fv("StereoInvert").get_asBool();
3720 settings.m_SubtitleCached = false;
3728 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3733 /// \brief Sets the settings for a particular video file
3734 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3738 if (NULL == m_pDB.get()) return ;
3739 if (NULL == m_pDS.get()) return ;
3740 int idFile = AddFile(strFilenameAndPath);
3743 CStdString strSQL = StringUtils::Format("select * from settings where idFile=%i", idFile);
3744 m_pDS->query( strSQL.c_str() );
3745 if (m_pDS->num_rows() > 0)
3749 strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3750 "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3751 "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3752 "DeinterlaceMode=%i,",
3753 setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3754 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3755 setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3756 setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3757 setting.m_DeinterlaceMode);
3759 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);
3761 m_pDS->exec(strSQL.c_str());
3767 strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3768 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3769 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3770 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3771 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode,StereoMode,StereoInvert) "
3773 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)",
3774 idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3775 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3776 setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3777 setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3778 setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3779 setting.m_DeinterlaceMode, setting.m_StereoMode, setting.m_StereoInvert);
3780 m_pDS->exec(strSQL.c_str());
3785 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3789 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3791 for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3792 SetArtForItem(mediaId, mediaType, i->first, i->second);
3795 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3799 if (NULL == m_pDB.get()) return;
3800 if (NULL == m_pDS.get()) return;
3802 // don't set <foo>.<bar> art types - these are derivative types from parent items
3803 if (artType.find('.') != string::npos)
3806 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());
3807 m_pDS->query(sql.c_str());
3810 int artId = m_pDS->fv(0).get_asInt();
3812 sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3813 m_pDS->exec(sql.c_str());
3818 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());
3819 m_pDS->exec(sql.c_str());
3824 CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3828 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3832 if (NULL == m_pDB.get()) return false;
3833 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3835 CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3836 m_pDS2->query(sql.c_str());
3837 while (!m_pDS2->eof())
3839 art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3843 return !art.empty();
3847 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3852 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3854 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());
3855 return GetSingleValue(query, m_pDS2);
3858 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::string &artType)
3860 return ExecuteQuery(PrepareSQL("DELETE FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str()));
3863 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::set<std::string> &artTypes)
3866 for (set<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
3867 result &= RemoveArtForItem(mediaId, mediaType, *i);
3872 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3876 if (NULL == m_pDB.get()) return false;
3877 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3879 // get all seasons for this show
3880 CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3881 m_pDS2->query(sql.c_str());
3883 vector< pair<int, int> > seasons;
3884 while (!m_pDS2->eof())
3886 seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3891 for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3893 map<string, string> art;
3894 GetArtForItem(i->first, "season", art);
3895 seasonArt.insert(make_pair(i->second,art));
3901 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3906 bool CVideoDatabase::GetArtTypes(const std::string &mediaType, std::vector<std::string> &artTypes)
3910 if (NULL == m_pDB.get()) return false;
3911 if (NULL == m_pDS.get()) return false;
3913 CStdString sql = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str());
3914 int numRows = RunQuery(sql);
3916 return numRows == 0;
3918 while (!m_pDS->eof())
3920 artTypes.push_back(m_pDS->fv(0).get_asString());
3928 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str());
3933 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3934 /// \retval Returns true if the stack times exist, false otherwise.
3935 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> ×)
3939 // obtain the FileID (if it exists)
3940 int idFile = GetFileId(filePath);
3941 if (idFile < 0) return false;
3942 if (NULL == m_pDB.get()) return false;
3943 if (NULL == m_pDS.get()) return false;
3944 // ok, now obtain the settings for this file
3945 CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3946 m_pDS->query( strSQL.c_str() );
3947 if (m_pDS->num_rows() > 0)
3948 { // get the video settings info
3949 CStdStringArray timeString;
3951 StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3953 for (unsigned int i = 0; i < timeString.size(); i++)
3955 times.push_back(atoi(timeString[i].c_str()));
3956 timeTotal += atoi(timeString[i].c_str());
3959 return (timeTotal > 0);
3965 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3970 /// \brief Sets the stack times for a particular video file
3971 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> ×)
3975 if (NULL == m_pDB.get()) return ;
3976 if (NULL == m_pDS.get()) return ;
3977 int idFile = AddFile(filePath);
3981 // delete any existing items
3982 m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3985 CStdString timeString = StringUtils::Format("%i", times[0]);
3986 for (unsigned int i = 1; i < times.size(); i++)
3987 timeString += StringUtils::Format(",%i", times[i]);
3989 m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3993 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3997 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
3999 if(URIUtils::IsMultiPath(strPath))
4001 vector<CStdString> paths;
4002 CMultiPathDirectory::GetPaths(strPath, paths);
4004 for(unsigned i=0;i<paths.size();i++)
4005 RemoveContentForPath(paths[i], progress);
4010 if (NULL == m_pDB.get()) return ;
4011 if (NULL == m_pDS.get()) return ;
4015 progress->SetHeading(700);
4016 progress->SetLine(0, "");
4017 progress->SetLine(1, 313);
4018 progress->SetLine(2, 330);
4019 progress->SetPercentage(0);
4020 progress->StartModal();
4021 progress->ShowProgressBar(true);
4023 vector< pair<int,string> > paths;
4024 GetSubPaths(strPath, paths);
4026 for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
4028 bool bMvidsChecked=false;
4031 progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
4032 progress->Progress();
4035 if (HasTvShowInfo(i->second))
4036 DeleteTvShow(i->second);
4039 CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
4040 m_pDS2->query(strSQL.c_str());
4043 strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4044 m_pDS2->query(strSQL.c_str());
4045 bMvidsChecked = true;
4047 while (!m_pDS2->eof())
4049 CStdString strMoviePath;
4050 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
4051 ConstructPath(strMoviePath, i->second, strFileName);
4052 if (HasMovieInfo(strMoviePath))
4053 DeleteMovie(strMoviePath);
4054 if (HasMusicVideoInfo(strMoviePath))
4055 DeleteMusicVideo(strMoviePath);
4057 if (m_pDS2->eof() && !bMvidsChecked)
4059 strSQL =PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4060 m_pDS2->query(strSQL.c_str());
4061 bMvidsChecked = true;
4065 m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
4071 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
4077 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
4079 // if we have a multipath, set scraper for all contained paths too
4080 if(URIUtils::IsMultiPath(filePath))
4082 vector<CStdString> paths;
4083 CMultiPathDirectory::GetPaths(filePath, paths);
4085 for(unsigned i=0;i<paths.size();i++)
4086 SetScraperForPath(paths[i],scraper,settings);
4091 if (NULL == m_pDB.get()) return ;
4092 if (NULL == m_pDS.get()) return ;
4094 int idPath = AddPath(filePath);
4100 if (settings.exclude)
4101 { //NB See note in ::GetScraperForPath about strContent=='none'
4102 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
4105 { // catch clearing content, but not excluding
4106 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
4110 CStdString content = TranslateContent(scraper->Content());
4111 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);
4113 m_pDS->exec(strSQL.c_str());
4117 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4121 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
4125 if (NULL == m_pDB.get()) return false;
4126 if (NULL == m_pDS.get()) return false;
4128 CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
4129 if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
4131 bool found = m_pDS->fv(0).get_asInt() > 0;
4137 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
4145 CArtItem() { art_id = 0; media_id = 0; };
4153 void CVideoDatabase::UpdateTables(int iVersion)
4157 m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4158 m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4161 { // add seasons table
4162 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4163 // insert all seasons for each show
4164 m_pDS->query("SELECT idShow FROM tvshow");
4165 while (!m_pDS->eof())
4167 CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4172 " JOIN tvshowlinkepisode ON"
4173 " episode.idEpisode=tvshowlinkepisode.idEpisode"
4174 " WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4175 m_pDS2->exec(sql.c_str());
4176 // and the "all seasons node"
4177 sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4178 m_pDS2->exec(sql.c_str());
4184 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4186 CMediaSettings::Get().SetVideoNeedsUpdate(63);
4187 CSettings::Get().Save();
4190 { // add idShow to episode table
4191 m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4192 m_pDS->query("SELECT idEpisode FROM episode");
4193 while (!m_pDS->eof())
4195 int idEpisode = m_pDS->fv(0).get_asInt();
4196 CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4197 m_pDS2->exec(update.c_str());
4200 m_pDS->exec("DROP TABLE tvshowlinkepisode");
4204 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4205 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4208 { // add idSet to movie table
4209 m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4210 m_pDS->query("SELECT idMovie FROM movie");
4211 while (!m_pDS->eof())
4213 int idMovie = m_pDS->fv(0).get_asInt();
4214 CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4215 m_pDS2->exec(sql.c_str());
4218 m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4221 { // update old art URLs
4222 m_pDS->query("select art_id,url from art where url like 'image://%%'");
4223 vector< pair<int, string> > art;
4224 while (!m_pDS->eof())
4226 art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4230 for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4231 m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4234 { // update URL encoded paths
4235 m_pDS->query("select idFile, strFilename from files");
4236 vector< pair<int, string> > files;
4237 while (!m_pDS->eof())
4239 files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4244 for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4246 std::string filename = i->second;
4247 bool update = URIUtils::UpdateUrlEncoding(filename) &&
4248 (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4252 m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4256 { // Update thumb to poster or banner as applicable
4257 CTextureDatabase db;
4260 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')");
4261 vector<CArtItem> art;
4262 while (!m_pDS->eof())
4264 CTextureDetails details;
4265 if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4268 item.art_id = m_pDS->fv(0).get_asInt();
4269 item.art_url = m_pDS->fv(1).get_asString();
4270 item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4271 item.media_id = m_pDS->fv(2).get_asInt();
4272 item.media_type = m_pDS->fv(3).get_asString();
4273 if (item.art_type != "thumb")
4274 art.push_back(item);
4279 for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4281 if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4282 m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4284 m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4289 { // update the runtime columns
4290 vector< pair<string, int> > tables;
4291 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4292 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4293 tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4294 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4296 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);
4297 m_pDS->query(sql.c_str());
4298 vector< pair<int, int> > videos;
4299 while (!m_pDS->eof())
4301 int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4303 videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4307 for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4308 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));
4313 m_pDS->exec("ALTER TABLE settings ADD StereoMode integer");
4314 m_pDS->exec("ALTER TABLE settings ADD StereoInvert bool");
4317 m_pDS->exec("ALTER TABLE streamdetails ADD strStereoMode text");
4320 int CVideoDatabase::GetSchemaVersion() const
4325 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4327 SScanSettings settings;
4328 bool foundDirectly = false;
4329 ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4330 if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4331 return false; // episodes
4332 return settings.parent_name_root; // shows, movies, musicvids
4335 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4337 if(URIUtils::IsMultiPath(strPath))
4339 vector<CStdString> paths;
4340 CMultiPathDirectory::GetPaths(strPath, paths);
4343 for(unsigned i=0;i<paths.size();i++)
4344 ret |= GetPlayCounts(paths[i], items);
4349 if (URIUtils::IsPlugin(strPath))
4352 pathID = GetPathId(url.GetWithoutFilename());
4355 pathID = GetPathId(strPath);
4357 return false; // path (and thus files) aren't in the database
4362 if (NULL == m_pDB.get()) return false;
4363 if (NULL == m_pDS.get()) return false;
4365 // TODO: also test a single query for the above and below
4366 CStdString sql = PrepareSQL(
4368 " files.strFilename, files.playCount,"
4369 " bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4371 " LEFT JOIN bookmark ON"
4372 " files.idFile = bookmark.idFile AND bookmark.type = %i"
4373 " WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4375 if (RunQuery(sql) <= 0)
4378 items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4379 while (!m_pDS->eof())
4382 ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4383 CFileItemPtr item = items.Get(path);
4386 item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4387 if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4389 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4390 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4391 item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4400 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4405 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4407 int id = GetFileId(item);
4409 return 0; // not in db, so not watched
4414 if (NULL == m_pDB.get()) return -1;
4415 if (NULL == m_pDS.get()) return -1;
4417 CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4419 if (m_pDS->query(strSQL.c_str()))
4421 // there should only ever be one row returned
4422 if (m_pDS->num_rows() == 1)
4423 count = m_pDS->fv(0).get_asInt();
4430 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4435 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4437 if (NULL == m_pDB.get()) return;
4438 if (NULL == m_pDS.get()) return;
4439 if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4442 if (type == VIDEODB_CONTENT_TVSHOWS)
4443 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);
4444 else if (type == VIDEODB_CONTENT_MOVIES)
4445 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);
4449 m_pDS->exec(exec.c_str());
4451 if (type == VIDEODB_CONTENT_TVSHOWS)
4452 AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4453 else if (type == VIDEODB_CONTENT_MOVIES)
4454 AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4458 CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4462 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4465 if (item.HasProperty("original_listitem_url") &&
4466 URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4468 CFileItem item2(item);
4469 item2.SetPath(item.GetProperty("original_listitem_url").asString());
4470 id = AddFile(item2);
4477 // and mark as watched
4480 if (NULL == m_pDB.get()) return ;
4481 if (NULL == m_pDS.get()) return ;
4486 if (!date.IsValid())
4487 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4489 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4493 if (!date.IsValid())
4494 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4496 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4499 m_pDS->exec(strSQL.c_str());
4501 // We only need to announce changes to video items in the library
4502 if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4504 // Only provide the "playcount" value if it has actually changed
4505 if (item.GetVideoInfoTag()->m_playCount != count)
4508 data["playcount"] = count;
4509 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4512 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4517 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4521 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4523 SetPlayCount(item, GetPlayCount(item) + 1);
4526 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4528 SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4531 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4535 if (NULL == m_pDB.get()) return ;
4536 if (NULL == m_pDS.get()) return ;
4538 if (iType == VIDEODB_CONTENT_MOVIES)
4540 CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4543 else if (iType == VIDEODB_CONTENT_EPISODES)
4545 CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4546 content = "episode";
4548 else if (iType == VIDEODB_CONTENT_TVSHOWS)
4550 CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4553 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4555 CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4556 content = "musicvideo";
4558 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4560 CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4561 CStdString strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4562 m_pDS->exec(strSQL.c_str());
4565 if (!content.empty())
4567 SetSingleValue(iType, idMovie, FieldTitle, strNewMovieTitle);
4568 AnnounceUpdate(content, idMovie);
4573 CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4577 bool CVideoDatabase::UpdateVideoSortTitle(int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType /* = VIDEODB_CONTENT_MOVIES */)
4581 if (NULL == m_pDB.get() || NULL == m_pDS.get())
4583 if (iType != VIDEODB_CONTENT_MOVIES && iType != VIDEODB_CONTENT_TVSHOWS)
4586 CStdString content = "movie";
4587 if (iType == VIDEODB_CONTENT_TVSHOWS)
4590 if (SetSingleValue(iType, idDb, FieldSortTitle, strNewSortTitle))
4592 AnnounceUpdate(content, idDb);
4598 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());
4604 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4605 void CVideoDatabase::EraseVideoSettings()
4609 CLog::Log(LOGINFO, "Deleting settings information for all movies");
4610 m_pDS->exec("delete from settings");
4614 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4618 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4620 return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4623 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4625 return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4628 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4630 return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4633 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4637 if (NULL == m_pDB.get()) return false;
4638 if (NULL == m_pDS.get()) return false;
4641 Filter extFilter = filter;
4642 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4644 if (idContent == VIDEODB_CONTENT_MOVIES)
4646 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4647 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());
4648 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",
4649 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4651 else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4653 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4654 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4655 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",
4656 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4658 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4660 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4661 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());
4662 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",
4663 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4670 if (idContent == VIDEODB_CONTENT_MOVIES)
4672 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4673 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());
4674 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",
4675 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4676 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4678 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4680 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4681 extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4682 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4683 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4685 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4687 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4688 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());
4689 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",
4690 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4691 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4699 extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4700 extFilter.group.clear();
4701 extFilter.order.clear();
4703 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4705 CVideoDbUrl videoUrl;
4706 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4709 int iRowsFound = RunQuery(strSQL);
4710 if (iRowsFound <= 0)
4711 return iRowsFound == 0;
4715 CFileItemPtr pItem(new CFileItem());
4716 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4723 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4725 map<int, pair<CStdString,int> > mapItems;
4726 map<int, pair<CStdString,int> >::iterator it;
4727 while (!m_pDS->eof())
4729 int id = m_pDS->fv(0).get_asInt();
4730 CStdString str = m_pDS->fv(1).get_asString();
4732 // was this already found?
4733 it = mapItems.find(id);
4734 if (it == mapItems.end())
4737 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
4739 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4740 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
4741 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4742 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
4749 for (it = mapItems.begin(); it != mapItems.end(); ++it)
4751 CFileItemPtr pItem(new CFileItem(it->second.first));
4752 pItem->GetVideoInfoTag()->m_iDbId = it->first;
4753 pItem->GetVideoInfoTag()->m_type = type;
4755 CVideoDbUrl itemUrl = videoUrl;
4756 CStdString path = StringUtils::Format("%ld/", it->first);
4757 itemUrl.AppendPath(path);
4758 pItem->SetPath(itemUrl.ToString());
4760 pItem->m_bIsFolder = true;
4761 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4762 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
4763 if (!items.Contains(pItem->GetPath()))
4765 pItem->SetLabelPreformated(true);
4772 while (!m_pDS->eof())
4774 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4775 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
4776 pItem->GetVideoInfoTag()->m_type = type;
4778 CVideoDbUrl itemUrl = videoUrl;
4779 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
4780 itemUrl.AppendPath(path);
4781 pItem->SetPath(itemUrl.ToString());
4783 pItem->m_bIsFolder = true;
4784 pItem->SetLabelPreformated(true);
4785 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4786 { // fv(3) is the number of videos watched, fv(2) is the total number. We set the playcount
4787 // only if the number of videos watched is equal to the total number (i.e. every video watched)
4788 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
4799 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4804 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4806 CStdString mediaType;
4807 if (idContent == VIDEODB_CONTENT_MOVIES)
4808 mediaType = "movie";
4809 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4810 mediaType = "tvshow";
4811 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4812 mediaType = "musicvideo";
4818 if (NULL == m_pDB.get()) return false;
4819 if (NULL == m_pDS.get()) return false;
4821 CStdString strSQL = "SELECT %s FROM taglinks ";
4823 Filter extFilter = filter;
4824 extFilter.fields = "tag.idTag, tag.strTag";
4825 extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
4827 if (idContent == (int)VIDEODB_CONTENT_MOVIES)
4828 extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
4830 extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
4831 extFilter.AppendGroup("taglinks.idTag");
4835 extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
4836 extFilter.group.clear();
4837 extFilter.order.clear();
4839 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4841 // parse the base path to get additional filters
4842 CVideoDbUrl videoUrl;
4843 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4846 int iRowsFound = RunQuery(strSQL);
4847 if (iRowsFound <= 0)
4848 return iRowsFound == 0;
4852 CFileItemPtr pItem(new CFileItem());
4853 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4860 while (!m_pDS->eof())
4862 int idTag = m_pDS->fv(0).get_asInt();
4864 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4865 pItem->m_bIsFolder = true;
4866 pItem->GetVideoInfoTag()->m_iDbId = idTag;
4867 pItem->GetVideoInfoTag()->m_type = "tag";
4869 CVideoDbUrl itemUrl = videoUrl;
4870 CStdString path = StringUtils::Format("%ld/", idTag);
4871 itemUrl.AppendPath(path);
4872 pItem->SetPath(itemUrl.ToString());
4874 if (!items.Contains(pItem->GetPath()))
4876 pItem->SetLabelPreformated(true);
4888 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4893 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool ignoreSingleMovieSets /* = false */)
4895 if (idContent != VIDEODB_CONTENT_MOVIES)
4898 return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
4901 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
4905 if (NULL == m_pDB.get()) return false;
4906 if (NULL == m_pDS.get()) return false;
4908 CVideoDbUrl videoUrl;
4909 if (!videoUrl.FromString(strBaseDir))
4912 Filter setFilter = filter;
4913 setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
4914 if (!setFilter.order.empty())
4915 setFilter.order += ",";
4916 setFilter.order += "sets.idSet";
4918 if (!GetMoviesByWhere(strBaseDir, setFilter, items))
4922 if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
4932 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4937 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4941 if (NULL == m_pDB.get()) return false;
4942 if (NULL == m_pDS.get()) return false;
4944 CVideoDbUrl videoUrl;
4945 if (!videoUrl.FromString(strBaseDir))
4948 CStdString strSQL = "select %s from musicvideoview ";
4949 Filter extFilter = filter;
4950 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
4951 extFilter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
4952 extFilter.AppendJoin(PrepareSQL("join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
4953 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4955 extFilter.fields += " path.strPath";
4956 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath");
4960 videoUrl.AddOption("artistid", idArtist);
4962 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
4966 extFilter.fields = "COUNT(1)";
4967 extFilter.group.clear();
4968 extFilter.order.clear();
4970 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4972 if (!BuildSQL(videoUrl.ToString(), strSQL, extFilter, strSQL, videoUrl))
4975 int iRowsFound = RunQuery(strSQL);
4976 if (iRowsFound <= 0)
4977 return iRowsFound == 0;
4981 CFileItemPtr pItem(new CFileItem());
4982 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4989 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4991 map<int, pair<CStdString,CStdString> > mapAlbums;
4992 map<int, pair<CStdString,CStdString> >::iterator it;
4993 while (!m_pDS->eof())
4995 int lidMVideo = m_pDS->fv(1).get_asInt();
4996 CStdString strAlbum = m_pDS->fv(0).get_asString();
4997 it = mapAlbums.find(lidMVideo);
4998 // was this genre already found?
4999 if (it == mapAlbums.end())
5002 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5003 mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5009 for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5011 if (!it->second.first.empty())
5013 CFileItemPtr pItem(new CFileItem(it->second.first));
5015 CVideoDbUrl itemUrl = videoUrl;
5016 CStdString path = StringUtils::Format("%ld/", it->first);
5017 itemUrl.AppendPath(path);
5018 pItem->SetPath(itemUrl.ToString());
5020 pItem->m_bIsFolder=true;
5021 pItem->SetLabelPreformated(true);
5022 if (!items.Contains(pItem->GetPath()))
5024 pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5032 while (!m_pDS->eof())
5034 if (!m_pDS->fv(0).get_asString().empty())
5036 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5038 CVideoDbUrl itemUrl = videoUrl;
5039 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(1).get_asInt());
5040 itemUrl.AppendPath(path);
5041 pItem->SetPath(itemUrl.ToString());
5043 pItem->m_bIsFolder=true;
5044 pItem->SetLabelPreformated(true);
5045 if (!items.Contains(pItem->GetPath()))
5047 pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5056 // CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5061 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5066 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5068 return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5071 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5073 return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5076 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5078 if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5079 { // set thumbs - ideally this should be in the normal thumb setting routines
5080 for (int i = 0; i < items.Size() && !countOnly; i++)
5082 CFileItemPtr pItem = items[i];
5083 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5084 pItem->SetIconImage("DefaultArtist.png");
5086 pItem->SetIconImage("DefaultActor.png");
5093 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5095 if (NULL == m_pDB.get()) return false;
5096 if (NULL == m_pDS.get()) return false;
5100 // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5101 // point. This means that we *MUST* filter these levels as you'll get double ups. Ideally we'd allow playcount
5102 // to filter through as we normally do for tvshows to save this happening.
5103 // Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5104 // Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5107 // General routine that the other actor/director/writer routines call
5109 // get primary genres for movies
5111 Filter extFilter = filter;
5112 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5114 if (idContent == VIDEODB_CONTENT_MOVIES)
5116 strSQL = "select %s from actors ";
5117 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5118 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",
5119 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5121 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5123 strSQL = "select %s from actors ";
5124 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5125 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",
5126 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5128 else if (idContent == VIDEODB_CONTENT_EPISODES)
5130 strSQL = "select %s from actors ";
5131 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5132 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",
5133 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5135 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5137 strSQL = "select %s from actors ";
5138 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5139 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",
5140 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5147 if (idContent == VIDEODB_CONTENT_MOVIES)
5149 strSQL ="select %s from actors ";
5150 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5151 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",
5152 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5153 extFilter.AppendGroup("actors.idActor");
5155 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5157 strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5158 extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5159 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5160 type.c_str(), type.c_str(), type.c_str()));
5162 else if (idContent == VIDEODB_CONTENT_EPISODES)
5164 strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5165 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5166 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5167 type.c_str(), type.c_str(), type.c_str()));
5168 extFilter.AppendGroup("actors.idActor");
5170 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5172 strSQL = "select %s from actors ";
5173 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5174 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",
5175 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5176 extFilter.AppendGroup("actors.idActor");
5184 extFilter.fields = "COUNT(1)";
5185 extFilter.group.clear();
5186 extFilter.order.clear();
5188 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5190 CVideoDbUrl videoUrl;
5191 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5195 unsigned int time = XbmcThreads::SystemClockMillis();
5196 if (!m_pDS->query(strSQL.c_str())) return false;
5197 CLog::Log(LOGDEBUG, "%s - query took %i ms",
5198 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5199 int iRowsFound = m_pDS->num_rows();
5200 if (iRowsFound == 0)
5208 CFileItemPtr pItem(new CFileItem());
5209 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5216 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5218 map<int, CActor> mapActors;
5219 map<int, CActor>::iterator it;
5221 while (!m_pDS->eof())
5223 int idActor = m_pDS->fv(0).get_asInt();
5225 actor.name = m_pDS->fv(1).get_asString();
5226 actor.thumb = m_pDS->fv(2).get_asString();
5227 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5228 actor.playcount = m_pDS->fv(3).get_asInt();
5229 it = mapActors.find(idActor);
5230 // is this actor already known?
5231 if (it == mapActors.end())
5234 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5235 mapActors.insert(pair<int, CActor>(idActor, actor));
5241 for (it=mapActors.begin();it != mapActors.end();++it)
5243 CFileItemPtr pItem(new CFileItem(it->second.name));
5245 CVideoDbUrl itemUrl = videoUrl;
5246 CStdString path = StringUtils::Format("%ld/", it->first);
5247 itemUrl.AppendPath(path);
5248 pItem->SetPath(itemUrl.ToString());
5250 pItem->m_bIsFolder=true;
5251 pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5252 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5253 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5254 pItem->GetVideoInfoTag()->m_type = type;
5260 while (!m_pDS->eof())
5264 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5266 CVideoDbUrl itemUrl = videoUrl;
5267 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5268 itemUrl.AppendPath(path);
5269 pItem->SetPath(itemUrl.ToString());
5271 pItem->m_bIsFolder=true;
5272 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5273 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5274 pItem->GetVideoInfoTag()->m_type = type;
5275 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5277 // fv(4) is the number of videos watched, fv(3) is the total number. We set the playcount
5278 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5279 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5281 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5282 pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5289 CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5290 return items.Size() > 0;
5295 CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5296 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5303 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5308 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5312 if (NULL == m_pDB.get()) return false;
5313 if (NULL == m_pDS.get()) return false;
5316 Filter extFilter = filter;
5317 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5319 if (idContent == VIDEODB_CONTENT_MOVIES)
5321 strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5322 extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5324 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5326 strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5327 extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5329 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5331 strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5332 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5340 if (idContent == VIDEODB_CONTENT_MOVIES)
5342 strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5343 extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5344 extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5346 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5347 strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5348 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5350 strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5351 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5352 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5358 CVideoDbUrl videoUrl;
5359 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5362 int iRowsFound = RunQuery(strSQL);
5363 if (iRowsFound <= 0)
5364 return iRowsFound == 0;
5366 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5368 map<int, pair<CStdString,int> > mapYears;
5369 map<int, pair<CStdString,int> >::iterator it;
5370 while (!m_pDS->eof())
5373 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5376 time.SetFromDateString(m_pDS->fv(0).get_asString());
5377 lYear = time.GetYear();
5379 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5380 lYear = m_pDS->fv(0).get_asInt();
5381 it = mapYears.find(lYear);
5382 if (it == mapYears.end())
5385 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5387 CStdString year = StringUtils::Format("%d", lYear);
5388 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5389 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5391 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5398 for (it=mapYears.begin();it != mapYears.end();++it)
5402 CFileItemPtr pItem(new CFileItem(it->second.first));
5404 CVideoDbUrl itemUrl = videoUrl;
5405 CStdString path = StringUtils::Format("%ld/", it->first);
5406 itemUrl.AppendPath(path);
5407 pItem->SetPath(itemUrl.ToString());
5409 pItem->m_bIsFolder=true;
5410 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5411 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5417 while (!m_pDS->eof())
5420 CStdString strLabel;
5421 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5424 time.SetFromDateString(m_pDS->fv(0).get_asString());
5425 lYear = time.GetYear();
5426 strLabel = StringUtils::Format("%i",lYear);
5428 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5430 lYear = m_pDS->fv(0).get_asInt();
5431 strLabel = m_pDS->fv(0).get_asString();
5438 CFileItemPtr pItem(new CFileItem(strLabel));
5440 CVideoDbUrl itemUrl = videoUrl;
5441 CStdString path = StringUtils::Format("%ld/", lYear);
5442 itemUrl.AppendPath(path);
5443 pItem->SetPath(itemUrl.ToString());
5445 pItem->m_bIsFolder=true;
5446 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5448 // fv(2) is the number of videos watched, fv(1) is the total number. We set the playcount
5449 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5450 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5453 // take care of dupes ..
5454 if (!items.Contains(pItem->GetPath()))
5466 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5471 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5475 if (NULL == m_pDB.get()) return false;
5476 if (NULL == m_pDS.get()) return false;
5478 // look for duplicate show titles and stack them into a list
5481 CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5482 CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5483 if (!m_pDS->query(strSQL.c_str())) return false;
5484 int iRows = m_pDS->num_rows();
5485 if (iRows == 0) return false; // this should never happen!
5487 { // more than one show, so stack them up
5489 while (!m_pDS->eof())
5491 strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5494 strIn[strIn.size() - 1] = ')'; // replace last , with )
5501 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5506 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5510 if (NULL == m_pDB.get()) return false;
5511 if (NULL == m_pDS.get()) return false;
5513 // parse the base path to get additional filters
5514 CVideoDbUrl videoUrl;
5515 if (!videoUrl.FromString(strBaseDir))
5518 CStdString strIn = PrepareSQL("= %i", idShow);
5519 GetStackedTvShowList(idShow, strIn);
5521 CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5523 "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5524 "seasons.idSeason, "
5525 "count(1), count(files.playCount) "
5526 "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);
5529 filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5530 "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5531 "JOIN files ON files.idFile = episodeview.idFile "
5532 "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5533 "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5534 filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5535 filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5537 videoUrl.AddOption("tvshowid", idShow);
5540 videoUrl.AddOption("actorid", idActor);
5541 else if (idDirector != -1)
5542 videoUrl.AddOption("directorid", idDirector);
5543 else if (idGenre != -1)
5544 videoUrl.AddOption("genreid", idGenre);
5545 else if (idYear != -1)
5546 videoUrl.AddOption("year", idYear);
5548 if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5551 int iRowsFound = RunQuery(strSQL);
5552 if (iRowsFound <= 0)
5553 return iRowsFound == 0;
5555 // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5556 CStdString showTitle = m_pDS->fv(2).get_asString();
5557 CStdString showPlot = m_pDS->fv(3).get_asString();
5558 CStdString showPremiered = m_pDS->fv(4).get_asString();
5559 CStdString showStudio = m_pDS->fv(6).get_asString();
5560 CStdString showMPAARating = m_pDS->fv(7).get_asString();
5562 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5564 map<int, CSeason> mapSeasons;
5565 map<int, CSeason>::iterator it;
5566 while (!m_pDS->eof())
5568 int iSeason = m_pDS->fv(0).get_asInt();
5569 it = mapSeasons.find(iSeason);
5571 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5576 if (it == mapSeasons.end())
5579 season.path = m_pDS->fv(1).get_asString();
5580 season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5581 season.id = m_pDS->fv(8).get_asInt();
5582 season.numEpisodes = m_pDS->fv(9).get_asInt();
5583 season.numWatched = m_pDS->fv(10).get_asInt();
5584 mapSeasons.insert(make_pair(iSeason, season));
5590 for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5592 int iSeason = it->first;
5593 CStdString strLabel;
5595 strLabel = g_localizeStrings.Get(20381);
5597 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5598 CFileItemPtr pItem(new CFileItem(strLabel));
5600 CVideoDbUrl itemUrl = videoUrl;
5601 CStdString strDir = StringUtils::Format("%ld/", it->first);
5602 itemUrl.AppendPath(strDir);
5603 pItem->SetPath(itemUrl.ToString());
5605 pItem->m_bIsFolder=true;
5606 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5607 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5608 pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5609 pItem->GetVideoInfoTag()->m_type = "season";
5610 pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5611 pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5612 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5613 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5614 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5615 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5616 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5617 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5618 pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5619 pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5620 pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5621 pItem->SetProperty("watchedepisodes", it->second.numWatched);
5622 pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5623 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5624 pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5625 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5631 while (!m_pDS->eof())
5633 int iSeason = m_pDS->fv(0).get_asInt();
5634 CStdString strLabel;
5636 strLabel = g_localizeStrings.Get(20381);
5638 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5639 CFileItemPtr pItem(new CFileItem(strLabel));
5641 CVideoDbUrl itemUrl = videoUrl;
5642 CStdString strDir = StringUtils::Format("%ld/", iSeason);
5643 itemUrl.AppendPath(strDir);
5644 pItem->SetPath(itemUrl.ToString());
5646 pItem->m_bIsFolder=true;
5647 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5648 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5649 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5650 pItem->GetVideoInfoTag()->m_type = "season";
5651 pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5652 pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5653 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5654 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5655 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5656 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5657 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5658 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5659 int totalEpisodes = m_pDS->fv(9).get_asInt();
5660 int watchedEpisodes = m_pDS->fv(10).get_asInt();
5661 pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5662 pItem->SetProperty("totalepisodes", totalEpisodes);
5663 pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5664 pItem->SetProperty("watchedepisodes", watchedEpisodes);
5665 pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5666 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5667 pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5668 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5675 // now add any linked movies
5676 if (getLinkedMovies)
5679 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5680 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5681 CFileItemList movieItems;
5682 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
5684 if (movieItems.Size() > 0)
5685 items.Append(movieItems);
5692 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5697 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5699 if (NULL == m_pDB.get() || NULL == m_pDS.get())
5702 if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5705 SortDescription sorting = sortDescription;
5706 if (sortDescription.sortBy == SortByFile ||
5707 sortDescription.sortBy == SortByTitle ||
5708 sortDescription.sortBy == SortBySortTitle ||
5709 sortDescription.sortBy == SortByLabel ||
5710 sortDescription.sortBy == SortByDateAdded ||
5711 sortDescription.sortBy == SortByRating ||
5712 sortDescription.sortBy == SortByYear ||
5713 sortDescription.sortBy == SortByLastPlayed ||
5714 sortDescription.sortBy == SortByPlaycount)
5715 sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5717 bool success = false;
5720 case MediaTypeMovie:
5721 success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5724 case MediaTypeTvShow:
5725 success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
5728 case MediaTypeEpisode:
5729 success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
5732 case MediaTypeMusicVideo:
5733 success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
5740 items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
5744 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5746 CVideoDbUrl videoUrl;
5747 if (!videoUrl.FromString(strBaseDir))
5750 return GetItems(strBaseDir, videoUrl.GetType(), videoUrl.GetItemType(), items, filter, sortDescription);
5753 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, const CStdString &mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5755 VIDEODB_CONTENT_TYPE contentType;
5756 if (mediaType.Equals("movies"))
5757 contentType = VIDEODB_CONTENT_MOVIES;
5758 else if (mediaType.Equals("tvshows"))
5760 if (itemType.Equals("episodes"))
5761 contentType = VIDEODB_CONTENT_EPISODES;
5763 contentType = VIDEODB_CONTENT_TVSHOWS;
5765 else if (mediaType.Equals("musicvideos"))
5766 contentType = VIDEODB_CONTENT_MUSICVIDEOS;
5770 return GetItems(strBaseDir, contentType, itemType, items, filter, sortDescription);
5773 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, VIDEODB_CONTENT_TYPE mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5775 if (itemType.Equals("movies") && (mediaType == VIDEODB_CONTENT_MOVIES || mediaType == VIDEODB_CONTENT_MOVIE_SETS))
5776 return GetMoviesByWhere(strBaseDir, filter, items, sortDescription);
5777 else if (itemType.Equals("tvshows") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5778 return GetTvShowsByWhere(strBaseDir, filter, items, sortDescription);
5779 else if (itemType.Equals("musicvideos") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5780 return GetMusicVideosByWhere(strBaseDir, filter, items, true, sortDescription);
5781 else if (itemType.Equals("episodes") && mediaType == VIDEODB_CONTENT_EPISODES)
5782 return GetEpisodesByWhere(strBaseDir, filter, items, true, sortDescription);
5783 else if (itemType.Equals("seasons") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5784 return GetSeasonsNav(strBaseDir, items);
5785 else if (itemType.Equals("genres"))
5786 return GetGenresNav(strBaseDir, items, mediaType, filter);
5787 else if (itemType.Equals("years"))
5788 return GetYearsNav(strBaseDir, items, mediaType, filter);
5789 else if (itemType.Equals("actors"))
5790 return GetActorsNav(strBaseDir, items, mediaType, filter);
5791 else if (itemType.Equals("directors"))
5792 return GetDirectorsNav(strBaseDir, items, mediaType, filter);
5793 else if (itemType.Equals("writers"))
5794 return GetWritersNav(strBaseDir, items, mediaType, filter);
5795 else if (itemType.Equals("studios"))
5796 return GetStudiosNav(strBaseDir, items, mediaType, filter);
5797 else if (itemType.Equals("sets"))
5798 return GetSetsNav(strBaseDir, items, mediaType, filter);
5799 else if (itemType.Equals("countries"))
5800 return GetCountriesNav(strBaseDir, items, mediaType, filter);
5801 else if (itemType.Equals("tags"))
5802 return GetTagsNav(strBaseDir, items, mediaType, filter);
5803 else if (itemType.Equals("artists") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5804 return GetActorsNav(strBaseDir, items, mediaType, filter);
5805 else if (itemType.Equals("albums") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5806 return GetMusicVideoAlbumsNav(strBaseDir, items, -1, filter);
5811 CStdString CVideoDatabase::GetItemById(const CStdString &itemType, int id)
5813 if (itemType.Equals("genres"))
5814 return GetGenreById(id);
5815 else if (itemType.Equals("years"))
5816 return StringUtils::Format("%d", id);
5817 else if (itemType.Equals("actors") || itemType.Equals("directors") || itemType.Equals("artists"))
5818 return GetPersonById(id);
5819 else if (itemType.Equals("studios"))
5820 return GetStudioById(id);
5821 else if (itemType.Equals("sets"))
5822 return GetSetById(id);
5823 else if (itemType.Equals("countries"))
5824 return GetCountryById(id);
5825 else if (itemType.Equals("tags"))
5826 return GetTagById(id);
5827 else if (itemType.Equals("albums"))
5828 return GetMusicVideoAlbumById(id);
5833 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
5834 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
5835 int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
5836 const SortDescription &sortDescription /* = SortDescription() */)
5838 CVideoDbUrl videoUrl;
5839 if (!videoUrl.FromString(strBaseDir))
5843 videoUrl.AddOption("genreid", idGenre);
5844 else if (idCountry > 0)
5845 videoUrl.AddOption("countryid", idCountry);
5846 else if (idStudio > 0)
5847 videoUrl.AddOption("studioid", idStudio);
5848 else if (idDirector > 0)
5849 videoUrl.AddOption("directorid", idDirector);
5850 else if (idYear > 0)
5851 videoUrl.AddOption("year", idYear);
5852 else if (idActor > 0)
5853 videoUrl.AddOption("actorid", idActor);
5855 videoUrl.AddOption("setid", idSet);
5857 videoUrl.AddOption("tagid", idTag);
5860 return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
5863 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5870 if (NULL == m_pDB.get()) return false;
5871 if (NULL == m_pDS.get()) return false;
5873 // parse the base path to get additional filters
5874 CVideoDbUrl videoUrl;
5875 Filter extFilter = filter;
5876 SortDescription sorting = sortDescription;
5877 if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
5882 CStdString strSQL = "select %s from movieview ";
5883 CStdString strSQLExtra;
5884 if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
5887 // Apply the limiting directly here if there's no special sorting but limiting
5888 if (extFilter.limit.empty() &&
5889 sorting.sortBy == SortByNone &&
5890 (sorting.limitStart > 0 || sorting.limitEnd > 0))
5892 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
5893 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
5896 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
5898 int iRowsFound = RunQuery(strSQL);
5899 if (iRowsFound <= 0)
5900 return iRowsFound == 0;
5902 // store the total value of items as a property
5903 if (total < iRowsFound)
5905 items.SetProperty("total", total);
5907 DatabaseResults results;
5908 results.reserve(iRowsFound);
5910 if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
5913 // get data from returned rows
5914 items.Reserve(results.size());
5915 const query_data &data = m_pDS->get_result_set().records;
5916 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
5918 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
5919 const dbiplus::sql_record* const record = data.at(targetRow);
5921 CVideoInfoTag movie = GetDetailsForMovie(record);
5922 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
5923 g_passwordManager.bMasterUser ||
5924 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
5926 CFileItemPtr pItem(new CFileItem(movie));
5928 CVideoDbUrl itemUrl = videoUrl;
5929 CStdString path = StringUtils::Format("%ld", movie.m_iDbId);
5930 itemUrl.AppendPath(path);
5931 pItem->SetPath(itemUrl.ToString());
5933 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
5944 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5949 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
5950 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
5951 const SortDescription &sortDescription /* = SortDescription() */)
5953 CVideoDbUrl videoUrl;
5954 if (!videoUrl.FromString(strBaseDir))
5958 videoUrl.AddOption("genreid", idGenre);
5959 else if (idStudio != -1)
5960 videoUrl.AddOption("studioid", idStudio);
5961 else if (idDirector != -1)
5962 videoUrl.AddOption("directorid", idDirector);
5963 else if (idYear != -1)
5964 videoUrl.AddOption("year", idYear);
5965 else if (idActor != -1)
5966 videoUrl.AddOption("actorid", idActor);
5967 else if (idTag != -1)
5968 videoUrl.AddOption("tagid", idTag);
5971 return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
5974 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5980 if (NULL == m_pDB.get()) return false;
5981 if (NULL == m_pDS.get()) return false;
5985 CStdString strSQL = "SELECT %s FROM tvshowview ";
5986 CVideoDbUrl videoUrl;
5987 CStdString strSQLExtra;
5988 Filter extFilter = filter;
5989 SortDescription sorting = sortDescription;
5990 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
5993 // Apply the limiting directly here if there's no special sorting but limiting
5994 if (extFilter.limit.empty() &&
5995 sorting.sortBy == SortByNone &&
5996 (sorting.limitStart > 0 || sorting.limitEnd > 0))
5998 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
5999 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6002 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6004 int iRowsFound = RunQuery(strSQL);
6005 if (iRowsFound <= 0)
6006 return iRowsFound == 0;
6008 // store the total value of items as a property
6009 if (total < iRowsFound)
6011 items.SetProperty("total", total);
6013 DatabaseResults results;
6014 results.reserve(iRowsFound);
6015 if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
6018 // get data from returned rows
6019 items.Reserve(results.size());
6020 const query_data &data = m_pDS->get_result_set().records;
6021 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6023 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6024 const dbiplus::sql_record* const record = data.at(targetRow);
6026 CFileItemPtr pItem(new CFileItem());
6027 CVideoInfoTag movie = GetDetailsForTvShow(record, false, pItem.get());
6028 if ((CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6029 g_passwordManager.bMasterUser ||
6030 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video"))) &&
6031 (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
6033 pItem->SetFromVideoInfoTag(movie);
6035 CVideoDbUrl itemUrl = videoUrl;
6036 CStdString path = StringUtils::Format("%ld/", record->at(0).get_asInt());
6037 itemUrl.AppendPath(path);
6038 pItem->SetPath(itemUrl.ToString());
6040 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6045 Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6053 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6058 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6060 if (maintainSortOrder)
6062 // save current sort order
6063 for (int i = 0; i < items.Size(); i++)
6064 items[i]->m_iprogramCount = i;
6069 case VIDEODB_CONTENT_TVSHOWS:
6072 items.Sort(SortBySortTitle, SortOrderAscending);
6075 while (i < items.Size())
6077 CFileItemPtr pItem = items.Get(i);
6078 CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6079 CStdString strFanArt = pItem->GetArt("fanart");
6082 bool bStacked = false;
6083 while (j < items.Size())
6085 CFileItemPtr jItem = items.Get(j);
6087 // matching title? append information
6088 if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6090 if (jItem->GetVideoInfoTag()->m_premiered !=
6091 pItem->GetVideoInfoTag()->m_premiered)
6098 // increment episode counts
6099 pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6100 pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6101 pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6102 pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6103 pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6105 // adjust lastplayed
6106 if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6107 pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6109 // check for fanart if not already set
6110 if (strFanArt.empty())
6111 strFanArt = jItem->GetArt("fanart");
6113 // remove duplicate entry
6116 // no match? exit loop
6120 // update playcount and fanart
6123 pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6124 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6125 if (!strFanArt.empty())
6126 pItem->SetArt("fanart", strFanArt);
6128 // increment i to j which is the next item
6133 // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6134 // so that if we eventually want to stack during scan we can utilize it.
6136 case VIDEODB_CONTENT_EPISODES:
6138 // sort by ShowTitle, Episode, Filename
6139 items.Sort(SortByEpisodeNumber, SortOrderAscending);
6142 while (i < items.Size())
6144 CFileItemPtr pItem = items.Get(i);
6145 CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6146 int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6147 int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6148 //CStdString strFanArt = pItem->GetArt("fanart");
6150 // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6151 CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6152 bool bDvdFolder = StringUtils::EndsWithNoCase(strFileNameAndPath, "video_ts.ifo");
6154 vector<CStdString> paths;
6155 paths.push_back(strFileNameAndPath);
6156 CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6159 int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6160 while (j < items.Size())
6162 CFileItemPtr jItem = items.Get(j);
6163 const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6164 CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6166 CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6167 // compare path, season, episode
6170 jTag->m_strPath.Equals(strPath) &&
6171 jTag->m_iSeason == iSeason &&
6172 jTag->m_iEpisode == iEpisode
6175 // keep checking to see if this is dvd folder
6178 bDvdFolder = StringUtils::EndsWithNoCase(jFileNameAndPath, "video_ts.ifo");
6179 // if we have a dvd folder, we stack differently
6182 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6184 paths.push_back(jFileNameAndPath);
6188 // increment playcount
6189 iPlayCount += jTag->m_playCount;
6191 // episodes dont have fanart yet
6192 //if (strFanArt.empty())
6193 // strFanArt = jItem->GetArt("fanart");
6195 paths.push_back(jFileNameAndPath);
6199 // remove duplicate entry
6203 // no match? exit loop
6207 // update playcount and fanart if we have a stacked entry
6208 if (paths.size() > 1)
6210 CStackDirectory dir;
6211 CStdString strStack;
6212 dir.ConstructStackPath(paths, strStack);
6213 pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6214 pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6215 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6217 // episodes dont have fanart yet
6218 //if (!strFanArt.empty())
6219 // pItem->SetArt("fanart", strFanArt);
6221 // increment i to j which is the next item
6227 // stack other types later
6231 if (maintainSortOrder)
6233 // restore original sort order - essential for smartplaylists
6234 items.Sort(SortByProgramCount, SortOrderAscending);
6238 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6240 CVideoDbUrl videoUrl;
6241 if (!videoUrl.FromString(strBaseDir))
6247 strIn = PrepareSQL("= %i", idShow);
6248 GetStackedTvShowList(idShow, strIn);
6250 videoUrl.AddOption("tvshowid", idShow);
6252 videoUrl.AddOption("season", idSeason);
6255 videoUrl.AddOption("genreid", idGenre);
6256 else if (idYear !=-1)
6257 videoUrl.AddOption("year", idYear);
6258 else if (idActor != -1)
6259 videoUrl.AddOption("actorid", idActor);
6261 else if (idYear != -1)
6262 videoUrl.AddOption("year", idYear);
6264 if (idDirector != -1)
6265 videoUrl.AddOption("directorid", idDirector);
6268 bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6270 if (idSeason == -1 && idShow != -1)
6271 { // add any linked movies
6273 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6274 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6275 CFileItemList movieItems;
6276 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
6278 if (movieItems.Size() > 0)
6279 items.Append(movieItems);
6285 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6292 if (NULL == m_pDB.get()) return false;
6293 if (NULL == m_pDS.get()) return false;
6297 CStdString strSQL = "select %s from episodeview ";
6298 CVideoDbUrl videoUrl;
6299 CStdString strSQLExtra;
6300 Filter extFilter = filter;
6301 SortDescription sorting = sortDescription;
6302 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6305 // Apply the limiting directly here if there's no special sorting but limiting
6306 if (extFilter.limit.empty() &&
6307 sorting.sortBy == SortByNone &&
6308 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6310 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6311 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6314 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6316 int iRowsFound = RunQuery(strSQL);
6317 if (iRowsFound <= 0)
6318 return iRowsFound == 0;
6320 // store the total value of items as a property
6321 if (total < iRowsFound)
6323 items.SetProperty("total", total);
6325 DatabaseResults results;
6326 results.reserve(iRowsFound);
6327 if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6330 // get data from returned rows
6331 items.Reserve(results.size());
6332 CLabelFormatter formatter("%H. %T", "");
6334 const query_data &data = m_pDS->get_result_set().records;
6335 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6337 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6338 const dbiplus::sql_record* const record = data.at(targetRow);
6340 CVideoInfoTag movie = GetDetailsForEpisode(record);
6341 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6342 g_passwordManager.bMasterUser ||
6343 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6345 CFileItemPtr pItem(new CFileItem(movie));
6346 formatter.FormatLabel(pItem.get());
6348 int idEpisode = record->at(0).get_asInt();
6350 CVideoDbUrl itemUrl = videoUrl;
6352 if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6353 path = StringUtils::Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6355 path = StringUtils::Format("%ld", idEpisode);
6356 itemUrl.AppendPath(path);
6357 pItem->SetPath(itemUrl.ToString());
6359 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6360 pItem->m_dateTime = movie.m_firstAired;
6361 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6372 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6377 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() */)
6379 CVideoDbUrl videoUrl;
6380 if (!videoUrl.FromString(strBaseDir))
6384 videoUrl.AddOption("genreid", idGenre);
6385 else if (idStudio != -1)
6386 videoUrl.AddOption("studioid", idStudio);
6387 else if (idDirector != -1)
6388 videoUrl.AddOption("directorid", idDirector);
6389 else if (idYear !=-1)
6390 videoUrl.AddOption("year", idYear);
6391 else if (idArtist != -1)
6392 videoUrl.AddOption("artistid", idArtist);
6393 else if (idTag != -1)
6394 videoUrl.AddOption("tagid", idTag);
6396 videoUrl.AddOption("albumid", idAlbum);
6399 return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6402 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6405 filter.order = "dateAdded desc, idMovie desc";
6406 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6407 return GetMoviesByWhere(strBaseDir, filter, items);
6410 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6413 filter.order = "dateAdded desc, idEpisode desc";
6414 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6415 return GetEpisodesByWhere(strBaseDir, filter, items, false);
6418 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6421 filter.order = "dateAdded desc, idMVideo desc";
6422 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6423 return GetMusicVideosByWhere(strBaseDir, filter, items);
6426 CStdString CVideoDatabase::GetGenreById(int id)
6428 return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6431 CStdString CVideoDatabase::GetCountryById(int id)
6433 return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6436 CStdString CVideoDatabase::GetSetById(int id)
6438 return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6441 CStdString CVideoDatabase::GetTagById(int id)
6443 return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6446 CStdString CVideoDatabase::GetPersonById(int id)
6448 return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6451 CStdString CVideoDatabase::GetStudioById(int id)
6453 return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6456 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6458 return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6461 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6463 return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6466 bool CVideoDatabase::HasSets() const
6470 if (NULL == m_pDB.get()) return false;
6471 if (NULL == m_pDS.get()) return false;
6473 m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6474 "JOIN sets ON sets.idSet = movieview.idSet "
6475 "GROUP BY movieview.idSet HAVING c>1");
6477 bool bResult = (m_pDS->num_rows() > 0);
6483 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6488 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6492 if (NULL == m_pDB.get()) return false;
6493 if (NULL == m_pDS2.get()) return false;
6495 // make sure we use m_pDS2, as this is called in loops using m_pDS
6496 CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6497 m_pDS2->query( strSQL.c_str() );
6501 result=m_pDS2->fv(0).get_asInt();
6508 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6513 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6516 sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6517 CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6520 return atoi(id.c_str());
6523 bool CVideoDatabase::HasContent()
6525 return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6526 HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6527 HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6530 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6532 bool result = false;
6535 if (NULL == m_pDB.get()) return false;
6536 if (NULL == m_pDS.get()) return false;
6539 if (type == VIDEODB_CONTENT_MOVIES)
6540 sql = "select count(1) from movie";
6541 else if (type == VIDEODB_CONTENT_TVSHOWS)
6542 sql = "select count(1) from tvshow";
6543 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6544 sql = "select count(1) from musicvideo";
6545 m_pDS->query( sql.c_str() );
6548 result = (m_pDS->fv(0).get_asInt() > 0);
6554 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6559 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6563 if (NULL == m_pDB.get()) return 0;
6564 if (NULL == m_pDS.get()) return 0;
6566 CStdString strSQL = StringUtils::Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6567 m_pDS->query( strSQL.c_str() );
6571 iResult = m_pDS->fv("nummovies").get_asInt();
6578 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6583 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6585 SScanSettings settings;
6586 return GetScraperForPath(strPath, settings);
6589 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6592 return GetScraperForPath(strPath, settings, dummy);
6595 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6597 foundDirectly = false;
6600 if (strPath.empty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6603 CStdString strPath2;
6605 if (URIUtils::IsMultiPath(strPath))
6606 strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6610 CStdString strPath1 = URIUtils::GetDirectory(strPath2);
6611 int idPath = GetPathId(strPath1);
6615 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);
6616 m_pDS->query( strSQL.c_str() );
6620 CONTENT_TYPE content = CONTENT_NONE;
6622 { // path is stored in db
6624 if (m_pDS->fv("path.exclude").get_asBool())
6626 settings.exclude = true;
6628 return ScraperPtr();
6630 settings.exclude = false;
6632 // try and ascertain scraper for this path
6633 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6634 StringUtils::ToLower(strcontent);
6635 content = TranslateContent(strcontent);
6637 //FIXME paths stored should not have empty strContent
6638 //assert(content != CONTENT_NONE);
6639 CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6642 if (!scraperID.empty() &&
6643 CAddonMgr::Get().GetAddon(scraperID, addon))
6645 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6647 return ScraperPtr();
6649 // store this path's content & settings
6650 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6651 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6652 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6653 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6657 if (content == CONTENT_NONE)
6658 { // this path is not saved in db
6659 // we must drill up until a scraper is configured
6660 CStdString strParent;
6661 while (URIUtils::GetParentPath(strPath1, strParent))
6665 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());
6666 m_pDS->query(strSQL.c_str());
6668 CONTENT_TYPE content = CONTENT_NONE;
6672 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6673 StringUtils::ToLower(strcontent);
6674 if (m_pDS->fv("path.exclude").get_asBool())
6676 settings.exclude = true;
6682 content = TranslateContent(strcontent);
6685 if (content != CONTENT_NONE &&
6686 CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6688 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6689 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6690 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6691 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6692 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6693 settings.exclude = false;
6697 strPath1 = strParent;
6702 if (!scraper || scraper->Content() == CONTENT_NONE)
6703 return ScraperPtr();
6705 if (scraper->Content() == CONTENT_TVSHOWS)
6707 settings.recurse = 0;
6708 if(settings.parent_name) // single show
6710 settings.parent_name_root = settings.parent_name = (iFound == 1);
6714 settings.parent_name_root = settings.parent_name = (iFound == 2);
6717 else if (scraper->Content() == CONTENT_MOVIES)
6719 settings.recurse = settings.recurse - (iFound-1);
6720 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6722 else if (scraper->Content() == CONTENT_MUSICVIDEOS)
6724 settings.recurse = settings.recurse - (iFound-1);
6725 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6730 return ScraperPtr();
6732 foundDirectly = (iFound == 1);
6737 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6739 return ScraperPtr();
6742 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
6744 SScanSettings settings;
6745 bool foundDirectly = false;
6746 ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
6749 if (scraper->Content() == CONTENT_TVSHOWS)
6750 { // check for episodes or seasons. Assumptions are:
6751 // 1. if episodes are in the path then we're in episodes.
6752 // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
6753 // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
6754 CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
6755 m_pDS->query( sql.c_str() );
6756 if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
6758 return foundDirectly ? "tvshows" : "seasons";
6760 return TranslateContent(scraper->Content());
6765 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
6771 if (NULL == m_pDB.get()) return;
6772 if (NULL == m_pDS.get()) return;
6774 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6775 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());
6777 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6778 m_pDS->query( strSQL.c_str() );
6780 while (!m_pDS->eof())
6782 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6783 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6784 *CMediaSourceSettings::Get().GetSources("video")))
6790 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6791 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6792 pItem->SetPath("videodb://movies/genres/"+ strDir);
6793 pItem->m_bIsFolder=true;
6801 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6805 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
6811 if (NULL == m_pDB.get()) return;
6812 if (NULL == m_pDS.get()) return;
6814 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6815 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());
6817 strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
6818 m_pDS->query( strSQL.c_str() );
6820 while (!m_pDS->eof())
6822 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6823 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6824 *CMediaSourceSettings::Get().GetSources("video")))
6830 CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
6831 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
6832 pItem->SetPath("videodb://movies/genres/"+ strDir);
6833 pItem->m_bIsFolder=true;
6841 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6845 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
6851 if (NULL == m_pDB.get()) return;
6852 if (NULL == m_pDS.get()) return;
6854 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6855 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());
6857 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6858 m_pDS->query( strSQL.c_str() );
6860 while (!m_pDS->eof())
6862 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6863 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6869 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6870 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6871 pItem->SetPath("videodb://tvshows/genres/"+ strDir);
6872 pItem->m_bIsFolder=true;
6880 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6884 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
6890 if (NULL == m_pDB.get()) return;
6891 if (NULL == m_pDS.get()) return;
6893 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6894 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());
6896 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());
6897 m_pDS->query( strSQL.c_str() );
6899 while (!m_pDS->eof())
6901 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6902 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6908 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6909 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6910 pItem->SetPath("videodb://movies/actors/"+ strDir);
6911 pItem->m_bIsFolder=true;
6919 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6923 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
6929 if (NULL == m_pDB.get()) return;
6930 if (NULL == m_pDS.get()) return;
6932 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6933 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());
6935 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());
6936 m_pDS->query( strSQL.c_str() );
6938 while (!m_pDS->eof())
6940 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6941 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6947 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6948 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6949 pItem->SetPath("videodb://tvshows/actors/"+ strDir);
6950 pItem->m_bIsFolder=true;
6958 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6962 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
6968 if (NULL == m_pDB.get()) return;
6969 if (NULL == m_pDS.get()) return;
6972 if (!strSearch.empty())
6973 strLike = "and actors.strActor like '%%%s%%'";
6974 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6975 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());
6977 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
6978 m_pDS->query( strSQL.c_str() );
6980 while (!m_pDS->eof())
6982 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6983 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6989 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6990 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6991 pItem->SetPath("videodb://musicvideos/artists/"+ strDir);
6992 pItem->m_bIsFolder=true;
7000 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7004 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
7010 if (NULL == m_pDB.get()) return;
7011 if (NULL == m_pDS.get()) return;
7013 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7014 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());
7016 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
7017 m_pDS->query( strSQL.c_str() );
7019 while (!m_pDS->eof())
7021 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7022 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7028 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7029 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7030 pItem->SetPath("videodb://musicvideos/genres/"+ strDir);
7031 pItem->m_bIsFolder=true;
7039 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7043 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7049 if (NULL == m_pDB.get()) return;
7050 if (NULL == m_pDS.get()) return;
7052 strSQL = StringUtils::Format("SELECT DISTINCT"
7053 " musicvideo.c%02d,"
7054 " musicvideo.idMVideo,"
7059 " files.idFile=musicvideo.idFile"
7061 " path.idPath=files.idPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
7062 if (!strSearch.empty())
7063 strSQL += PrepareSQL(" WHERE musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM, strSearch.c_str());
7065 m_pDS->query( strSQL.c_str() );
7067 while (!m_pDS->eof())
7069 if (m_pDS->fv(0).get_asString().empty())
7075 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7076 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7082 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7083 CStdString strDir = StringUtils::Format("%ld", m_pDS->fv(1).get_asInt());
7084 pItem->SetPath("videodb://musicvideos/titles/"+ strDir);
7085 pItem->m_bIsFolder=false;
7093 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7097 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7103 if (NULL == m_pDB.get()) return;
7104 if (NULL == m_pDS.get()) return;
7106 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7107 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());
7109 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());
7110 m_pDS->query( strSQL.c_str() );
7112 while (!m_pDS->eof())
7114 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7115 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7121 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7122 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7124 pItem->SetPath("videodb://"+ strDir);
7125 pItem->m_bIsFolder=false;
7133 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7137 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7144 if (NULL == m_pDB.get()) return false;
7145 if (NULL == m_pDS.get()) return false;
7149 CStdString strSQL = "select %s from musicvideoview ";
7150 CVideoDbUrl videoUrl;
7151 CStdString strSQLExtra;
7152 Filter extFilter = filter;
7153 SortDescription sorting = sortDescription;
7154 if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7157 // Apply the limiting directly here if there's no special sorting but limiting
7158 if (extFilter.limit.empty() &&
7159 sorting.sortBy == SortByNone &&
7160 (sorting.limitStart > 0 || sorting.limitEnd > 0))
7162 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7163 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7166 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7168 int iRowsFound = RunQuery(strSQL);
7169 if (iRowsFound <= 0)
7170 return iRowsFound == 0;
7172 // store the total value of items as a property
7173 if (total < iRowsFound)
7175 items.SetProperty("total", total);
7177 DatabaseResults results;
7178 results.reserve(iRowsFound);
7179 if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7182 // get data from returned rows
7183 items.Reserve(results.size());
7184 // get songs from returned subtable
7185 const query_data &data = m_pDS->get_result_set().records;
7186 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7188 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7189 const dbiplus::sql_record* const record = data.at(targetRow);
7191 CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7192 if (!checkLocks || CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7193 g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
7195 CFileItemPtr item(new CFileItem(musicvideo));
7197 CVideoDbUrl itemUrl = videoUrl;
7198 CStdString path = StringUtils::Format("%ld", record->at(0).get_asInt());
7199 itemUrl.AppendPath(path);
7200 item->SetPath(itemUrl.ToString());
7202 item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7213 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7218 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7222 if (NULL == m_pDB.get()) return 0;
7223 if (NULL == m_pDS.get()) return 0;
7225 CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7226 if (!m_pDS->query(strSQL.c_str())) return 0;
7228 if (m_pDS->num_rows() == 0)
7233 songIDs.reserve(m_pDS->num_rows());
7234 while (!m_pDS->eof())
7236 songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7240 return songIDs.size();
7244 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7249 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7255 if (NULL == m_pDB.get()) return false;
7256 if (NULL == m_pDS.get()) return false;
7258 // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7259 CStdString strSQL = StringUtils::Format("select * from musicvideoview where %s", strWhere.c_str());
7260 strSQL += PrepareSQL(" order by RANDOM() limit 1");
7261 CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7263 if (!m_pDS->query(strSQL.c_str()))
7265 int iRowsFound = m_pDS->num_rows();
7266 if (iRowsFound != 1)
7271 *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7272 CStdString path = StringUtils::Format("videodb://musicvideos/titles/%ld",item->GetVideoInfoTag()->m_iDbId);
7273 item->SetPath(path);
7274 idSong = m_pDS->fv("idMVideo").get_asInt();
7275 item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7281 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7286 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7290 if (NULL == m_pDB.get()) return -1;
7291 if (NULL == m_pDS.get()) return -1;
7294 if (strAlbum.empty() && strTitle.empty())
7295 { // we want to return matching artists only
7296 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7297 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());
7299 strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7302 { // we want to return the matching musicvideo
7303 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7304 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());
7306 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());
7308 m_pDS->query( strSQL.c_str() );
7313 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7314 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7320 int lResult = m_pDS->fv(0).get_asInt();
7326 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7331 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7337 if (NULL == m_pDB.get()) return;
7338 if (NULL == m_pDS.get()) return;
7340 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7341 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());
7343 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());
7344 m_pDS->query( strSQL.c_str() );
7346 while (!m_pDS->eof())
7348 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7349 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7355 int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7356 int setId = m_pDS->fv("movie.idSet").get_asInt();
7357 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7359 if (setId <= 0 || !CSettings::Get().GetBool("videolibrary.groupmoviesets"))
7360 path = StringUtils::Format("videodb://movies/titles/%i", movieId);
7362 path = StringUtils::Format("videodb://movies/sets/%i/%i", setId, movieId);
7363 pItem->SetPath(path);
7364 pItem->m_bIsFolder=false;
7372 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7376 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7382 if (NULL == m_pDB.get()) return;
7383 if (NULL == m_pDS.get()) return;
7385 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7386 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());
7388 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());
7389 m_pDS->query( strSQL.c_str() );
7391 while (!m_pDS->eof())
7393 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7394 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7400 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7401 CStdString strDir = StringUtils::Format("tvshows/titles/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7403 pItem->SetPath("videodb://"+ strDir);
7404 pItem->m_bIsFolder=true;
7405 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7413 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7417 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7423 if (NULL == m_pDB.get()) return;
7424 if (NULL == m_pDS.get()) return;
7426 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7427 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());
7429 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());
7430 m_pDS->query( strSQL.c_str() );
7432 while (!m_pDS->eof())
7434 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7435 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7441 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7442 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());
7443 pItem->SetPath(path);
7444 pItem->m_bIsFolder=false;
7452 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7456 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7458 // Alternative searching - not quite as fast though due to
7459 // retrieving all information
7460 // 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()));
7461 // GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
7466 if (NULL == m_pDB.get()) return;
7467 if (NULL == m_pDS.get()) return;
7469 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7470 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());
7472 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());
7473 m_pDS->query( strSQL.c_str() );
7475 while (!m_pDS->eof())
7477 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7478 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7484 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7485 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7487 pItem->SetPath("videodb://"+ strDir);
7488 pItem->m_bIsFolder=false;
7496 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7500 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7502 // Alternative searching - not quite as fast though due to
7503 // retrieving all information
7505 // 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());
7506 // 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());
7507 // GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
7513 if (NULL == m_pDB.get()) return;
7514 if (NULL == m_pDS.get()) return;
7516 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7517 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());
7519 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());
7520 m_pDS->query( strSQL.c_str() );
7522 while (!m_pDS->eof())
7524 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7525 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7531 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7532 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());
7533 pItem->SetPath(path);
7534 pItem->m_bIsFolder=false;
7542 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7546 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7552 if (NULL == m_pDB.get()) return;
7553 if (NULL == m_pDS.get()) return;
7555 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7556 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());
7558 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());
7560 m_pDS->query( strSQL.c_str() );
7562 while (!m_pDS->eof())
7564 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7565 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7571 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7572 CStdString path = StringUtils::Format("videodb://movies/titles/%ld", m_pDS->fv(0).get_asInt());
7573 pItem->SetPath(path);
7574 pItem->m_bIsFolder=false;
7584 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7588 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7594 if (NULL == m_pDB.get()) return;
7595 if (NULL == m_pDS.get()) return;
7597 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7598 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());
7600 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());
7602 m_pDS->query( strSQL.c_str() );
7604 while (!m_pDS->eof())
7606 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7607 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7613 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7614 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7616 pItem->SetPath("videodb://movies/directors/"+ strDir);
7617 pItem->m_bIsFolder=true;
7625 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7629 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7635 if (NULL == m_pDB.get()) return;
7636 if (NULL == m_pDS.get()) return;
7638 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7639 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());
7641 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());
7643 m_pDS->query( strSQL.c_str() );
7645 while (!m_pDS->eof())
7647 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7648 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7654 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7655 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7657 pItem->SetPath("videodb://tvshows/studios/"+ strDir);
7658 pItem->m_bIsFolder=true;
7666 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7670 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7676 if (NULL == m_pDB.get()) return;
7677 if (NULL == m_pDS.get()) return;
7679 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7680 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());
7682 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());
7684 m_pDS->query( strSQL.c_str() );
7686 while (!m_pDS->eof())
7688 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7689 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7695 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7696 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7698 pItem->SetPath("videodb://musicvideos/albums/"+ strDir);
7699 pItem->m_bIsFolder=true;
7707 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7711 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7713 CGUIDialogProgress *progress=NULL;
7716 if (NULL == m_pDB.get()) return;
7717 if (NULL == m_pDS.get()) return;
7719 unsigned int time = XbmcThreads::SystemClockMillis();
7720 CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
7721 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
7725 // find all the files
7726 CStdString sql = "SELECT files.idFile, files.strFileName, path.strPath FROM files, path WHERE files.idPath = path.idPath";
7731 RollbackTransaction();
7732 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7736 CStdString strPaths;
7737 for (std::set<int>::const_iterator it = paths->begin(); it != paths->end(); ++it)
7738 strPaths += StringUtils::Format(",%i", *it);
7739 sql += PrepareSQL(" AND path.idPath IN (%s)", strPaths.substr(1).c_str());
7742 m_pDS->query(sql.c_str());
7743 if (m_pDS->num_rows() == 0) return;
7747 handle->SetTitle(g_localizeStrings.Get(700));
7748 handle->SetText("");
7750 else if (showProgress)
7752 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
7755 progress->SetHeading(700);
7756 progress->SetLine(0, "");
7757 progress->SetLine(1, 313);
7758 progress->SetLine(2, 330);
7759 progress->SetPercentage(0);
7760 progress->StartModal();
7761 progress->ShowProgressBar(true);
7765 std::string filesToTestForDelete;
7767 int total = m_pDS->num_rows();
7770 while (!m_pDS->eof())
7772 std::string path = m_pDS->fv("path.strPath").get_asString();
7773 std::string fileName = m_pDS->fv("files.strFileName").get_asString();
7774 CStdString fullPath;
7775 ConstructPath(fullPath, path, fileName);
7777 // get the first stacked file
7778 if (URIUtils::IsStack(fullPath))
7779 fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
7781 // remove optical, non-existing files
7782 if (URIUtils::IsOnDVD(fullPath) || !CFile::Exists(fullPath, false))
7783 filesToTestForDelete += m_pDS->fv("files.idFile").get_asString() + ",";
7785 if (handle == NULL && progress != NULL)
7787 int percentage = current * 100 / total;
7788 if (percentage > progress->GetPercentage())
7790 progress->SetPercentage(percentage);
7791 progress->Progress();
7793 if (progress->IsCanceled())
7797 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7801 else if (handle != NULL)
7802 handle->SetPercentage(current * 100 / (float)total);
7809 std::string filesToDelete;
7811 // Add any files that don't have a valid idPath entry to the filesToDelete list.
7812 m_pDS->query("SELECT files.idFile FROM files WHERE NOT EXISTS (SELECT 1 FROM path WHERE path.idPath = files.idPath)");
7813 while (!m_pDS->eof())
7815 string file = m_pDS->fv("files.idFile").get_asString() + ",";
7816 filesToTestForDelete += file;
7817 filesToDelete += file;
7823 std::map<int, bool> pathsDeleteDecisions;
7824 std::vector<int> movieIDs;
7825 std::vector<int> tvshowIDs;
7826 std::vector<int> episodeIDs;
7827 std::vector<int> musicVideoIDs;
7829 if (!filesToTestForDelete.empty())
7831 StringUtils::TrimRight(filesToTestForDelete, ",");
7833 movieIDs = CleanMediaType("movie", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7834 episodeIDs = CleanMediaType("episode", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7835 musicVideoIDs = CleanMediaType("musicvideo", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7838 if (progress != NULL)
7840 progress->SetPercentage(100);
7841 progress->Progress();
7844 if (!filesToDelete.empty())
7846 filesToDelete = "(" + StringUtils::TrimRight(filesToDelete, ",") + ")";
7848 CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
7849 sql = "DELETE FROM files WHERE idFile IN " + filesToDelete;
7850 m_pDS->exec(sql.c_str());
7852 CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
7853 sql = "DELETE FROM streamdetails WHERE idFile IN " + filesToDelete;
7854 m_pDS->exec(sql.c_str());
7856 CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
7857 sql = "DELETE FROM bookmark WHERE idFile IN " + filesToDelete;
7858 m_pDS->exec(sql.c_str());
7860 CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
7861 sql = "DELETE FROM settings WHERE idFile IN " + filesToDelete;
7862 m_pDS->exec(sql.c_str());
7864 CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
7865 sql = "DELETE FROM stacktimes WHERE idFile IN " + filesToDelete;
7866 m_pDS->exec(sql.c_str());
7869 if (!movieIDs.empty())
7871 std::string moviesToDelete;
7872 for (std::vector<int>::const_iterator it = movieIDs.begin(); it != movieIDs.end(); ++it)
7873 moviesToDelete += StringUtils::Format("%i,", *it);
7874 moviesToDelete = "(" + StringUtils::TrimRight(moviesToDelete, ",") + ")";
7876 CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
7877 sql = "DELETE FROM movie WHERE idMovie IN " + moviesToDelete;
7878 m_pDS->exec(sql.c_str());
7880 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
7881 sql = "DELETE FROM actorlinkmovie WHERE idMovie IN " + moviesToDelete;
7882 m_pDS->exec(sql.c_str());
7884 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
7885 sql = "DELETE FROM directorlinkmovie WHERE idMovie IN " + moviesToDelete;
7886 m_pDS->exec(sql.c_str());
7888 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
7889 sql = "DELETE FROM writerlinkmovie WHERE idMovie IN " + moviesToDelete;
7890 m_pDS->exec(sql.c_str());
7892 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
7893 sql = "DELETE FROM genrelinkmovie WHERE idMovie IN " + moviesToDelete;
7894 m_pDS->exec(sql.c_str());
7896 CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
7897 sql = "DELETE FROM countrylinkmovie WHERE idMovie IN " + moviesToDelete;
7898 m_pDS->exec(sql.c_str());
7900 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
7901 sql = "DELETE FROM studiolinkmovie WHERE idMovie IN " + moviesToDelete;
7902 m_pDS->exec(sql.c_str());
7905 if (!episodeIDs.empty())
7907 std::string episodesToDelete;
7908 for (std::vector<int>::const_iterator it = episodeIDs.begin(); it != episodeIDs.end(); ++it)
7909 episodesToDelete += StringUtils::Format("%i,", *it);
7910 episodesToDelete = "(" + StringUtils::TrimRight(episodesToDelete, ",") + ")";
7912 CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
7913 sql = "DELETE FROM episode WHERE idEpisode IN " + episodesToDelete;
7914 m_pDS->exec(sql.c_str());
7916 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
7917 sql = "DELETE FROM actorlinkepisode WHERE idEpisode IN " + episodesToDelete;
7918 m_pDS->exec(sql.c_str());
7920 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
7921 sql = "DELETE FROM directorlinkepisode WHERE idEpisode IN " + episodesToDelete;
7922 m_pDS->exec(sql.c_str());
7924 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
7925 sql = "DELETE FROM writerlinkepisode WHERE idEpisode IN " + episodesToDelete;
7926 m_pDS->exec(sql.c_str());
7929 CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and have content set...", __FUNCTION__);
7930 sql = "SELECT path.idPath, path.strPath FROM path "
7931 "WHERE NOT ((strContent IS NULL OR strContent = '') "
7932 "AND (strSettings IS NULL OR strSettings = '') "
7933 "AND (strHash IS NULL OR strHash = '') "
7934 "AND (exclude IS NULL OR exclude != 1))";
7935 m_pDS->query(sql.c_str());
7937 while (!m_pDS->eof())
7939 std::map<int, bool>::const_iterator pathsDeleteDecision = pathsDeleteDecisions.find(m_pDS->fv(0).get_asInt());
7940 if ((pathsDeleteDecision != pathsDeleteDecisions.end() && pathsDeleteDecision->second) ||
7941 (pathsDeleteDecision == pathsDeleteDecisions.end() && !CDirectory::Exists(m_pDS->fv(1).get_asString(), false)))
7942 strIds += m_pDS->fv(0).get_asString() + ",";
7948 if (!strIds.empty())
7950 sql = PrepareSQL("DELETE FROM path WHERE idPath IN (%s)", StringUtils::TrimRight(strIds, ",").c_str());
7951 m_pDS->exec(sql.c_str());
7952 sql = "DELETE FROM tvshowlinkpath WHERE NOT EXISTS (SELECT 1 FROM path WHERE path.idPath = tvshowlinkpath.idPath)";
7953 m_pDS->exec(sql.c_str());
7956 CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
7957 sql = "DELETE FROM tvshow WHERE NOT EXISTS (SELECT 1 FROM tvshowlinkpath WHERE tvshowlinkpath.idShow = tvshow.idShow)";
7958 m_pDS->exec(sql.c_str());
7960 std::string tvshowsToDelete;
7961 sql = "SELECT tvshow.idShow FROM tvshow "
7962 "JOIN tvshowlinkpath ON tvshow.idShow = tvshowlinkpath.idShow "
7963 "JOIN path ON path.idPath = tvshowlinkpath.idPath "
7964 "WHERE NOT EXISTS (SELECT 1 FROM episode WHERE episode.idShow = tvshow.idShow) "
7965 "AND (path.strContent IS NULL OR path.strContent = '')";
7966 m_pDS->query(sql.c_str());
7967 while (!m_pDS->eof())
7969 tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
7970 tvshowsToDelete += m_pDS->fv(0).get_asString() + ",";
7974 if (!tvshowsToDelete.empty())
7976 sql = "DELETE FROM tvshow WHERE idShow IN (" + StringUtils::TrimRight(tvshowsToDelete, ",") + ")";
7977 m_pDS->exec(sql.c_str());
7979 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
7980 sql = "DELETE FROM actorlinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = actorlinktvshow.idShow)";
7981 m_pDS->exec(sql.c_str());
7983 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
7984 sql = "DELETE FROM directorlinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = directorlinktvshow.idShow)";
7985 m_pDS->exec(sql.c_str());
7987 CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
7988 sql = "DELETE FROM tvshowlinkpath WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = tvshowlinkpath.idShow)";
7989 m_pDS->exec(sql.c_str());
7991 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
7992 sql = "DELETE FROM genrelinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = genrelinktvshow.idShow)";
7993 m_pDS->exec(sql.c_str());
7995 CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
7996 sql = "DELETE FROM seasons WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = seasons.idShow)";
7997 m_pDS->exec(sql.c_str());
7999 CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
8000 sql = "DELETE FROM movielinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = movielinktvshow.idShow)";
8001 m_pDS->exec(sql.c_str());
8002 sql = "DELETE FROM movielinktvshow WHERE NOT EXISTS (SELECT 1 FROM movie WHERE movie.idMovie = movielinktvshow.idMovie)";
8003 m_pDS->exec(sql.c_str());
8006 if (!musicVideoIDs.empty())
8008 std::string musicVideosToDelete;
8009 for (std::vector<int>::const_iterator it = musicVideoIDs.begin(); it != musicVideoIDs.end(); ++it)
8010 musicVideosToDelete += StringUtils::Format("%i,", *it);
8011 musicVideosToDelete = "(" + StringUtils::TrimRight(musicVideosToDelete, ",") + ")";
8013 CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8014 sql = "DELETE FROM musicvideo WHERE idMVideo IN " + musicVideosToDelete;
8015 m_pDS->exec(sql.c_str());
8017 CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8018 sql = "DELETE FROM artistlinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8019 m_pDS->exec(sql.c_str());
8021 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8022 sql = "DELETE FROM directorlinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8023 m_pDS->exec(sql.c_str());
8025 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8026 sql = "DELETE FROM genrelinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8027 m_pDS->exec(sql.c_str());
8029 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8030 sql = "DELETE FROM studiolinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8031 m_pDS->exec(sql.c_str());
8034 CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8035 sql = StringUtils::Format("DELETE FROM path "
8036 "WHERE (strContent IS NULL OR strContent = '') "
8037 "AND (strSettings IS NULL OR strSettings = '') "
8038 "AND (strHash IS NULL OR strHash = '') "
8039 "AND (exclude IS NULL OR exclude != 1) "
8040 "AND NOT EXISTS (SELECT 1 FROM files WHERE files.idPath = path.idPath) "
8041 "AND NOT EXISTS (SELECT 1 FROM tvshowlinkpath WHERE tvshowlinkpath.idPath = path.idPath) "
8042 "AND NOT EXISTS (SELECT 1 FROM movie WHERE movie.c%02d = path.idPath) "
8043 "AND NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.c%02d = path.idPath) "
8044 "AND NOT EXISTS (SELECT 1 FROM episode WHERE episode.c%02d = path.idPath) "
8045 "AND NOT EXISTS (SELECT 1 FROM musicvideo WHERE musicvideo.c%02d = path.idPath)"
8046 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8047 m_pDS->exec(sql.c_str());
8049 CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8050 sql = "DELETE FROM genre "
8051 "WHERE NOT EXISTS (SELECT 1 FROM genrelinkmovie WHERE genrelinkmovie.idGenre = genre.idGenre) "
8052 "AND NOT EXISTS (SELECT 1 FROM genrelinktvshow WHERE genrelinktvshow.idGenre = genre.idGenre) "
8053 "AND NOT EXISTS (SELECT 1 FROM genrelinkmusicvideo WHERE genrelinkmusicvideo.idGenre = genre.idGenre)";
8054 m_pDS->exec(sql.c_str());
8056 CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8057 sql = "DELETE FROM country WHERE NOT EXISTS (SELECT 1 FROM countrylinkmovie WHERE countrylinkmovie.idCountry = country.idCountry)";
8058 m_pDS->exec(sql.c_str());
8060 CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8061 sql = "DELETE FROM actors "
8062 "WHERE NOT EXISTS (SELECT 1 FROM actorlinkmovie WHERE actorlinkmovie.idActor = actors.idActor) "
8063 "AND NOT EXISTS (SELECT 1 FROM directorlinkmovie WHERE directorlinkmovie.idDirector = actors.idActor) "
8064 "AND NOT EXISTS (SELECT 1 FROM writerlinkmovie WHERE writerlinkmovie.idWriter = actors.idActor) "
8065 "AND NOT EXISTS (SELECT 1 FROM actorlinktvshow WHERE actorlinktvshow.idActor = actors.idActor) "
8066 "AND NOT EXISTS (SELECT 1 FROM actorlinkepisode WHERE actorlinkepisode.idActor = actors.idActor) "
8067 "AND NOT EXISTS (SELECT 1 FROM directorlinktvshow WHERE directorlinktvshow.idDirector = actors.idActor) "
8068 "AND NOT EXISTS (SELECT 1 FROM directorlinkepisode WHERE directorlinkepisode.idDirector = actors.idActor) "
8069 "AND NOT EXISTS (SELECT 1 FROM writerlinkepisode WHERE writerlinkepisode.idWriter = actors.idActor) "
8070 "AND NOT EXISTS (SELECT 1 FROM artistlinkmusicvideo WHERE artistlinkmusicvideo.idArtist = actors.idActor) "
8071 "AND NOT EXISTS (SELECT 1 FROM directorlinkmusicvideo WHERE directorlinkmusicvideo.idDirector = actors.idActor)";
8072 m_pDS->exec(sql.c_str());
8074 CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8075 sql = "DELETE FROM studio "
8076 "WHERE NOT EXISTS (SELECT 1 FROM studiolinkmovie WHERE studiolinkmovie.idStudio = studio.idStudio) "
8077 "AND NOT EXISTS (SELECT 1 FROM studiolinkmusicvideo WHERE studiolinkmusicvideo.idStudio = studio.idStudio) "
8078 "AND NOT EXISTS (SELECT 1 FROM studiolinktvshow WHERE studiolinktvshow.idStudio = studio.idStudio)";
8079 m_pDS->exec(sql.c_str());
8081 CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8082 sql = "DELETE FROM sets WHERE NOT EXISTS (SELECT 1 FROM movie WHERE movie.idSet = sets.idSet)";
8083 m_pDS->exec(sql.c_str());
8085 CommitTransaction();
8088 handle->SetTitle(g_localizeStrings.Get(331));
8092 CUtil::DeleteVideoDatabaseDirectoryCache();
8094 time = XbmcThreads::SystemClockMillis() - time;
8095 CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8097 for (std::vector<int>::const_iterator it = movieIDs.begin(); it != movieIDs.end(); ++it)
8098 AnnounceRemove("movie", *it);
8100 for (std::vector<int>::const_iterator it = episodeIDs.begin(); it != episodeIDs.end(); ++it)
8101 AnnounceRemove("episode", *it);
8103 for (std::vector<int>::const_iterator it = tvshowIDs.begin(); it != tvshowIDs.end(); ++it)
8104 AnnounceRemove("tvshow", *it);
8106 for (std::vector<int>::const_iterator it = musicVideoIDs.begin(); it != musicVideoIDs.end(); ++it)
8107 AnnounceRemove("musicvideo", *it);
8111 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8112 RollbackTransaction();
8117 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8120 std::vector<int> CVideoDatabase::CleanMediaType(const std::string &mediaType, const std::string &cleanableFileIDs,
8121 std::map<int, bool> &pathsDeleteDecisions, std::string &deletedFileIDs, bool silent)
8123 std::vector<int> cleanedIDs;
8124 if (mediaType.empty() || cleanableFileIDs.empty())
8127 std::string table = mediaType;
8128 std::string idField;
8129 std::string parentPathIdField;
8130 bool isEpisode = false;
8131 if (mediaType == "movie")
8133 idField = "idMovie";
8134 parentPathIdField = StringUtils::Format("%s.c%02d", table.c_str(), VIDEODB_ID_PARENTPATHID);
8136 else if (mediaType == "episode")
8138 idField = "idEpisode";
8139 parentPathIdField = StringUtils::Format("tvshow.c%02d", VIDEODB_ID_TV_PARENTPATHID);
8142 else if (mediaType == "musicvideo")
8144 idField = "idMVideo";
8145 parentPathIdField = StringUtils::Format("%s.c%02d", table.c_str(), VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
8150 // now grab them media items
8151 std::string sql = PrepareSQL("SELECT %s.%s, %s.idFile, %s, path.idPath, parentPath.strPath, parentPath.useFolderNames FROM %s "
8152 "JOIN files ON files.idFile = %s.idFile "
8153 "JOIN path ON path.idPath = files.idPath ",
8154 table.c_str(), idField.c_str(), table.c_str(), parentPathIdField.c_str(), table.c_str(),
8158 sql += "JOIN tvshow ON tvshow.idShow = episode.idShow ";
8160 sql += PrepareSQL("JOIN path as parentPath ON parentPath.idPath = %s "
8161 "WHERE %s.idFile IN (%s)",
8162 parentPathIdField.c_str(),
8163 table.c_str(), cleanableFileIDs.c_str());
8165 // map of parent path ID to boolean pair (if not exists and user choice)
8166 std::map<int, std::pair<bool, bool> > parentPathsDeleteDecisions;
8167 m_pDS->query(sql.c_str());
8168 while (!m_pDS->eof())
8170 int parentPathID = m_pDS->fv(2).get_asInt();
8171 std::map<int, std::pair<bool, bool> >::const_iterator parentPathsDeleteDecision = parentPathsDeleteDecisions.find(parentPathID);
8173 if (parentPathsDeleteDecision == parentPathsDeleteDecisions.end())
8175 std::string parentPath = m_pDS->fv(4).get_asString();
8176 bool parentPathNotExists = !CDirectory::Exists(parentPath, false);
8177 // if the parent path exists, the file will be deleted without asking
8178 // if the parent path doesn't exist, ask the user whether to remove all items it contained
8179 if (parentPathNotExists)
8181 // in silent mode assume that the files are just temporarily missing
8186 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
8187 if (pDialog != NULL)
8189 CURL parentUrl(parentPath);
8190 pDialog->SetHeading(15012);
8191 pDialog->SetText(StringUtils::Format(g_localizeStrings.Get(15013), parentUrl.GetWithoutUserDetails().c_str()));
8192 pDialog->SetChoice(0, 15015);
8193 pDialog->SetChoice(1, 15014);
8195 //send message and wait for user input
8196 ThreadMessage tMsg = { TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, (unsigned int)g_windowManager.GetActiveWindow() };
8197 CApplicationMessenger::Get().SendMessage(tMsg, true);
8199 del = !pDialog->IsConfirmed();
8204 parentPathsDeleteDecisions.insert(make_pair(parentPathID, make_pair(parentPathNotExists, del)));
8206 // the only reason not to delete the file is if the parent path doesn't
8207 // exist and the user decided to delete all the items it contained
8208 else if (parentPathsDeleteDecision->second.first &&
8209 !parentPathsDeleteDecision->second.second)
8212 if (m_pDS->fv(5).get_asBool())
8213 pathsDeleteDecisions.insert(make_pair(m_pDS->fv(3).get_asInt(), del));
8217 deletedFileIDs += m_pDS->fv(1).get_asString() + ",";
8218 cleanedIDs.push_back(m_pDS->fv(0).get_asInt());
8228 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8231 CFileItemList items;
8232 GetTvShowsByWhere("videodb://tvshows/titles/", "", items);
8233 CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8234 CDirectory::Create(showPath);
8235 for (int i = 0; i < items.Size(); i++)
8237 // create a folder in this directory
8238 CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8239 CStdString TVFolder = URIUtils::AddFileToFolder(showPath, showName);
8240 if (CDirectory::Create(TVFolder))
8241 { // right - grab the episodes and dump them as well
8242 CFileItemList episodes;
8243 Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8244 GetEpisodesByWhere("videodb://tvshows/titles/", filter, episodes);
8245 for (int i = 0; i < episodes.Size(); i++)
8247 CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8248 CStdString episode = StringUtils::Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8250 CStdString episodePath = URIUtils::AddFileToFolder(TVFolder, episode);
8252 if (file.OpenForWrite(episodePath))
8259 GetMoviesByWhere("videodb://movies/titles/", "", items);
8260 CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8261 CDirectory::Create(moviePath);
8262 for (int i = 0; i < items.Size(); i++)
8264 CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8265 CStdString movie = StringUtils::Format("%s.avi", tag->m_strTitle.c_str());
8267 if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8272 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8274 CGUIDialogProgress *progress=NULL;
8277 if (NULL == m_pDB.get()) return;
8278 if (NULL == m_pDS.get()) return;
8279 if (NULL == m_pDS2.get()) return;
8281 // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8282 auto_ptr<Dataset> pDS;
8283 pDS.reset(m_pDB->CreateDataset());
8284 if (NULL == pDS.get()) return;
8286 auto_ptr<Dataset> pDS2;
8287 pDS2.reset(m_pDB->CreateDataset());
8288 if (NULL == pDS2.get()) return;
8290 // if we're exporting to a single folder, we export thumbs as well
8291 CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8292 CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8293 CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8294 CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8295 CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8296 CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8302 CDirectory::Remove(exportRoot);
8303 CDirectory::Create(exportRoot);
8304 CDirectory::Create(actorsDir);
8305 CDirectory::Create(moviesDir);
8306 CDirectory::Create(musicvideosDir);
8307 CDirectory::Create(tvshowsDir);
8310 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8312 CStdString sql = "select * from movieview";
8314 m_pDS->query(sql.c_str());
8318 progress->SetHeading(647);
8319 progress->SetLine(0, 650);
8320 progress->SetLine(1, "");
8321 progress->SetLine(2, "");
8322 progress->SetPercentage(0);
8323 progress->StartModal();
8324 progress->ShowProgressBar(true);
8327 int total = m_pDS->num_rows();
8330 // create our xml document
8331 CXBMCTinyXML xmlDoc;
8332 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8333 xmlDoc.InsertEndChild(decl);
8334 TiXmlNode *pMain = NULL;
8339 TiXmlElement xmlMainElement("videodb");
8340 pMain = xmlDoc.InsertEndChild(xmlMainElement);
8341 XMLUtils::SetInt(pMain,"version", GetExportVersion());
8344 while (!m_pDS->eof())
8346 CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8347 // strip paths to make them relative
8348 if (StringUtils::StartsWith(movie.m_strTrailer, movie.m_strPath))
8349 movie.m_strTrailer = movie.m_strTrailer.substr(movie.m_strPath.size());
8350 map<string, string> artwork;
8351 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8353 TiXmlElement additionalNode("art");
8354 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8355 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8356 movie.Save(pMain, "movie", true, &additionalNode);
8359 movie.Save(pMain, "movie", !singleFiles);
8361 // reset old skip state
8366 progress->SetLine(1, movie.m_strTitle);
8367 progress->SetPercentage(current * 100 / total);
8368 progress->Progress();
8369 if (progress->IsCanceled())
8377 CFileItem item(movie.m_strFileNameAndPath,false);
8378 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8380 if (!item.Exists(false))
8382 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8387 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8389 if (item.IsOpticalMediaFile())
8391 nfoFile = URIUtils::AddFileToFolder(
8392 URIUtils::GetParentPath(nfoFile),
8393 URIUtils::GetFileName(nfoFile));
8396 if (overwrite || !CFile::Exists(nfoFile, false))
8398 if(!xmlDoc.SaveFile(nfoFile))
8400 CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8401 bSkip = ExportSkipEntry(nfoFile);
8418 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8419 xmlDoc.InsertEndChild(decl);
8422 if (images && !bSkip)
8426 CStdString strFileName(movie.m_strTitle);
8427 if (movie.m_iYear > 0)
8428 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8429 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8431 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8433 CStdString savedThumb = item.GetLocalArt(i->first, false);
8434 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8437 ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8444 // find all musicvideos
8445 sql = "select * from musicvideoview";
8447 m_pDS->query(sql.c_str());
8449 total = m_pDS->num_rows();
8452 while (!m_pDS->eof())
8454 CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8455 map<string, string> artwork;
8456 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8458 TiXmlElement additionalNode("art");
8459 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8460 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8461 movie.Save(pMain, "musicvideo", true, &additionalNode);
8464 movie.Save(pMain, "musicvideo", !singleFiles);
8466 // reset old skip state
8471 progress->SetLine(1, movie.m_strTitle);
8472 progress->SetPercentage(current * 100 / total);
8473 progress->Progress();
8474 if (progress->IsCanceled())
8482 CFileItem item(movie.m_strFileNameAndPath,false);
8483 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8485 if (!item.Exists(false))
8487 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8492 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8494 if (overwrite || !CFile::Exists(nfoFile, false))
8496 if(!xmlDoc.SaveFile(nfoFile))
8498 CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8499 bSkip = ExportSkipEntry(nfoFile);
8516 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8517 xmlDoc.InsertEndChild(decl);
8519 if (images && !bSkip)
8523 CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8524 if (movie.m_iYear > 0)
8525 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8526 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8528 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8530 CStdString savedThumb = item.GetLocalArt(i->first, false);
8531 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8539 // repeat for all tvshows
8540 sql = "SELECT * FROM tvshowview";
8541 m_pDS->query(sql.c_str());
8543 total = m_pDS->num_rows();
8546 while (!m_pDS->eof())
8548 CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8550 map<int, map<string, string> > seasonArt;
8551 GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8553 map<string, string> artwork;
8554 if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8556 TiXmlElement additionalNode("art");
8557 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8558 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8559 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8561 TiXmlElement seasonNode("season");
8562 seasonNode.SetAttribute("num", i->first);
8563 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8564 XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8565 additionalNode.InsertEndChild(seasonNode);
8567 tvshow.Save(pMain, "tvshow", true, &additionalNode);
8570 tvshow.Save(pMain, "tvshow", !singleFiles);
8572 // reset old skip state
8577 progress->SetLine(1, tvshow.m_strTitle);
8578 progress->SetPercentage(current * 100 / total);
8579 progress->Progress();
8580 if (progress->IsCanceled())
8588 // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8589 if (URIUtils::IsMultiPath(tvshow.m_strPath))
8590 tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8592 CFileItem item(tvshow.m_strPath, true);
8593 if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8595 if (!item.Exists(false))
8597 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8602 CStdString nfoFile = URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo");
8604 if (overwrite || !CFile::Exists(nfoFile, false))
8606 if(!xmlDoc.SaveFile(nfoFile))
8608 CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8609 bSkip = ExportSkipEntry(nfoFile);
8626 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8627 xmlDoc.InsertEndChild(decl);
8629 if (images && !bSkip)
8632 item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8634 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8636 CStdString savedThumb = item.GetLocalArt(i->first, true);
8637 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8641 ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8643 // export season thumbs
8644 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8648 seasonThumb = "season-all";
8649 else if (i->first == 0)
8650 seasonThumb = "season-specials";
8652 seasonThumb = StringUtils::Format("season%02i", i->first);
8653 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8655 CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8656 if (!i->second.empty())
8657 CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8662 // now save the episodes from this show
8663 sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8664 pDS->query(sql.c_str());
8665 CStdString showDir(item.GetPath());
8669 CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8670 map<string, string> artwork;
8671 if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8673 TiXmlElement additionalNode("art");
8674 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8675 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8676 episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8678 else if (!singleFiles)
8679 episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8681 episode.Save(pMain, "episodedetails", !singleFiles);
8683 // multi-episode files need dumping to the same XML
8684 while (singleFiles && !pDS->eof() &&
8685 episode.m_iFileId == pDS->fv("idFile").get_asInt())
8687 episode = GetDetailsForEpisode(pDS, true);
8688 episode.Save(pMain, "episodedetails", !singleFiles);
8692 // reset old skip state
8695 CFileItem item(episode.m_strFileNameAndPath, false);
8696 if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8698 if (!item.Exists(false))
8700 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8705 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8707 if (overwrite || !CFile::Exists(nfoFile, false))
8709 if(!xmlDoc.SaveFile(nfoFile))
8711 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8712 bSkip = ExportSkipEntry(nfoFile);
8729 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8730 xmlDoc.InsertEndChild(decl);
8733 if (images && !bSkip)
8737 CStdString epName = StringUtils::Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8738 item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8740 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8742 CStdString savedThumb = item.GetLocalArt(i->first, false);
8743 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8746 ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8755 if (singleFiles && progress)
8757 progress->SetPercentage(100);
8758 progress->Progress();
8763 // now dump path info
8764 set<CStdString> paths;
8766 TiXmlElement xmlPathElement("paths");
8767 TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8768 for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8770 bool foundDirectly = false;
8771 SScanSettings settings;
8772 ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8773 if (info && foundDirectly)
8775 TiXmlElement xmlPathElement2("path");
8776 TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8777 XMLUtils::SetString(pPath,"url", *iter);
8778 XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8779 XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8780 XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8781 XMLUtils::SetString(pPath,"scraperpath", info->ID());
8784 xmlDoc.SaveFile(xmlFile);
8789 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8796 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8798 CStdString strPath(strDir);
8801 strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8802 if (!CDirectory::Exists(strPath))
8804 CDirectory::Create(strPath);
8805 CFile::SetHidden(strPath, true);
8809 for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8812 item.SetLabel(iter->strName);
8813 if (!iter->thumb.empty())
8815 CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8816 CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8821 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8823 CStdString strParent;
8824 URIUtils::GetParentPath(nfoFile,strParent);
8825 CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8827 bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8830 CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8832 CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8837 void CVideoDatabase::ImportFromXML(const CStdString &path)
8839 CGUIDialogProgress *progress=NULL;
8842 if (NULL == m_pDB.get()) return;
8843 if (NULL == m_pDS.get()) return;
8845 CXBMCTinyXML xmlDoc;
8846 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
8849 TiXmlElement *root = xmlDoc.RootElement();
8852 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8855 progress->SetHeading(648);
8856 progress->SetLine(0, 649);
8857 progress->SetLine(1, 330);
8858 progress->SetLine(2, "");
8859 progress->SetPercentage(0);
8860 progress->StartModal();
8861 progress->ShowProgressBar(true);
8865 XMLUtils::GetInt(root, "version", iVersion);
8867 CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
8869 TiXmlElement *movie = root->FirstChildElement();
8872 // first count the number of items...
8875 if (strnicmp(movie->Value(), "movie", 5)==0 ||
8876 strnicmp(movie->Value(), "tvshow", 6)==0 ||
8877 strnicmp(movie->Value(), "musicvideo",10)==0 )
8879 movie = movie->NextSiblingElement();
8882 CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
8883 CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
8884 CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
8885 CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
8886 CVideoInfoScanner scanner;
8887 // add paths first (so we have scraper settings available)
8888 TiXmlElement *path = root->FirstChildElement("paths");
8889 path = path->FirstChildElement();
8893 if (XMLUtils::GetString(path,"url",strPath) && !strPath.empty())
8897 if (XMLUtils::GetString(path,"content", content) && !content.empty())
8898 { // check the scraper exists, if so store the path
8901 XMLUtils::GetString(path,"scraperpath",id);
8902 if (CAddonMgr::Get().GetAddon(id, addon))
8904 SScanSettings settings;
8905 ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
8906 // FIXME: scraper settings are not exported?
8907 scraper->SetPathSettings(TranslateContent(content), "");
8908 XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
8909 XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
8910 SetScraperForPath(strPath,scraper,settings);
8913 path = path->NextSiblingElement();
8915 movie = root->FirstChildElement();
8919 if (strnicmp(movie->Value(), "movie", 5) == 0)
8922 CFileItem item(info);
8923 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
8924 CStdString filename = info.m_strTitle;
8925 if (info.m_iYear > 0)
8926 filename += StringUtils::Format("_%i", info.m_iYear);
8927 CFileItem artItem(item);
8928 artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
8929 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8930 item.SetArt(artItem.GetArt());
8931 scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
8934 else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
8937 CFileItem item(info);
8938 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
8939 CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
8940 if (info.m_iYear > 0)
8941 filename += StringUtils::Format("_%i", info.m_iYear);
8942 CFileItem artItem(item);
8943 artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
8944 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8945 item.SetArt(artItem.GetArt());
8946 scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
8949 else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
8951 // load the TV show in. NOTE: This deletes all episodes under the TV Show, which may not be
8952 // what we desire. It may make better sense to only delete (or even better, update) the show information
8954 URIUtils::AddSlashAtEnd(info.m_strPath);
8955 DeleteTvShow(info.m_strPath);
8956 CFileItem showItem(info);
8957 bool useFolders = info.m_basePath.empty() ? LookupByFolders(showItem.GetPath(), true) : false;
8958 CFileItem artItem(showItem);
8959 CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
8960 artItem.SetPath(artPath);
8961 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8962 showItem.SetArt(artItem.GetArt());
8963 int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
8965 map<int, map<string, string> > seasonArt;
8966 artItem.GetVideoInfoTag()->m_strPath = artPath;
8967 scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
8968 for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8970 int seasonID = AddSeason(showID, i->first);
8971 SetArtForItem(seasonID, "season", i->second);
8974 // now load the episodes
8975 TiXmlElement *episode = movie->FirstChildElement("episodedetails");
8978 // no need to delete the episode info, due to the above deletion
8981 CFileItem item(info);
8982 CStdString filename = StringUtils::Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
8983 CFileItem artItem(item);
8984 artItem.SetPath(GetSafeFile(artPath, filename));
8985 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8986 item.SetArt(artItem.GetArt());
8987 scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
8988 episode = episode->NextSiblingElement("episodedetails");
8991 movie = movie->NextSiblingElement();
8992 if (progress && total)
8994 progress->SetPercentage(current * 100 / total);
8995 progress->SetLine(2, info.m_strTitle);
8996 progress->Progress();
8997 if (progress->IsCanceled())
9007 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
9013 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
9015 if (!node) return false;
9016 const TiXmlNode *art = node->FirstChild();
9017 while (art && art->FirstChild())
9019 artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
9020 art = art->NextSibling();
9022 return !artwork.empty();
9025 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
9027 if (URIUtils::IsStack(strFileName) ||
9028 URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
9029 strDest = strFileName;
9031 strDest = URIUtils::AddFileToFolder(strPath, strFileName);
9034 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
9036 if (URIUtils::IsStack(strFileNameAndPath) || StringUtils::StartsWithNoCase(strFileNameAndPath, "rar://") || StringUtils::StartsWithNoCase(strFileNameAndPath, "zip://"))
9038 URIUtils::GetParentPath(strFileNameAndPath,strPath);
9039 strFileName = strFileNameAndPath;
9041 else if (URIUtils::IsPlugin(strFileNameAndPath))
9043 CURL url(strFileNameAndPath);
9044 strPath = url.GetWithoutFilename();
9045 strFileName = strFileNameAndPath;
9048 URIUtils::Split(strFileNameAndPath,strPath, strFileName);
9051 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
9053 SScanSettings settings;
9055 ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
9056 SetPathHash(strPath,"");
9059 if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
9061 if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
9063 CStdString strParent;
9064 URIUtils::GetParentPath(strPath,strParent);
9065 SetPathHash(strParent,"");
9070 bool CVideoDatabase::CommitTransaction()
9072 if (CDatabase::CommitTransaction())
9073 { // number of items in the db has likely changed, so recalculate
9074 g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
9075 g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
9076 g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
9082 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, int dbField, const std::string &strValue)
9087 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9090 string strTable, strField;
9091 if (type == VIDEODB_CONTENT_MOVIES)
9094 strField = "idMovie";
9096 else if (type == VIDEODB_CONTENT_TVSHOWS)
9098 strTable = "tvshow";
9099 strField = "idShow";
9101 else if (type == VIDEODB_CONTENT_EPISODES)
9103 strTable = "episode";
9104 strField = "idEpisode";
9106 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
9108 strTable = "musicvideo";
9109 strField = "idMVideo";
9112 if (strTable.empty())
9115 return SetSingleValue(strTable, StringUtils::Format("c%02u", dbField), strValue, strField, dbId);
9119 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
9124 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, Field dbField, const std::string &strValue)
9126 MediaType mediaType = DatabaseUtils::MediaTypeFromVideoContentType(type);
9127 if (mediaType == MediaTypeNone)
9130 int dbFieldIndex = DatabaseUtils::GetField(dbField, mediaType);
9131 if (dbFieldIndex < 0)
9134 return SetSingleValue(type, dbId, dbFieldIndex, strValue);
9137 bool CVideoDatabase::SetSingleValue(const std::string &table, const std::string &fieldName, const std::string &strValue,
9138 const std::string &conditionName /* = "" */, int conditionValue /* = -1 */)
9140 if (table.empty() || fieldName.empty())
9146 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9149 sql = PrepareSQL("UPDATE %s SET %s='%s'", table.c_str(), fieldName.c_str(), strValue.c_str());
9150 if (!conditionName.empty())
9151 sql += PrepareSQL(" WHERE %s=%u", conditionName.c_str(), conditionValue);
9152 if (m_pDS->exec(sql.c_str()) == 0)
9157 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, sql.c_str());
9162 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
9164 CStdString safeThumb(name);
9165 StringUtils::Replace(safeThumb, ' ', '_');
9166 return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
9169 void CVideoDatabase::AnnounceRemove(std::string content, int id)
9172 data["type"] = content;
9174 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9177 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9180 data["type"] = content;
9182 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9185 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9187 CStdString path(strPath);
9189 if(URIUtils::IsMultiPath(path))
9191 vector<CStdString> paths;
9192 CMultiPathDirectory::GetPaths(path, paths);
9194 for(unsigned i=0;i<paths.size();i++)
9195 GetItemsForPath(content, paths[i], items);
9197 return items.Size() > 0;
9200 int pathID = GetPathId(path);
9204 if (content == "movies")
9206 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9207 GetMoviesByWhere("videodb://movies/titles/", filter, items);
9209 else if (content == "episodes")
9211 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9212 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
9214 else if (content == "tvshows")
9216 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9217 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
9219 else if (content == "musicvideos")
9221 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9222 GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
9224 for (int i = 0; i < items.Size(); i++)
9225 items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9226 return items.Size() > 0;
9229 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9231 if (!videoUrl.IsValid())
9234 std::string type = videoUrl.GetType();
9235 std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9236 const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9237 CUrlOptions::UrlOptions::const_iterator option;
9239 if (type == "movies")
9241 option = options.find("genreid");
9242 if (option != options.end())
9244 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9245 filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9248 option = options.find("genre");
9249 if (option != options.end())
9251 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9252 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9255 option = options.find("countryid");
9256 if (option != options.end())
9258 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9259 filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9262 option = options.find("country");
9263 if (option != options.end())
9265 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9266 filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9269 option = options.find("studioid");
9270 if (option != options.end())
9272 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9273 filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9276 option = options.find("studio");
9277 if (option != options.end())
9279 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9280 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9283 option = options.find("directorid");
9284 if (option != options.end())
9286 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9287 filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9290 option = options.find("director");
9291 if (option != options.end())
9293 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9294 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9297 option = options.find("year");
9298 if (option != options.end())
9299 filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9301 option = options.find("actorid");
9302 if (option != options.end())
9304 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9305 filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9308 option = options.find("actor");
9309 if (option != options.end())
9311 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9312 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9315 option = options.find("setid");
9316 if (option != options.end())
9317 filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9319 option = options.find("set");
9320 if (option != options.end())
9322 filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9323 filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9326 option = options.find("tagid");
9327 if (option != options.end())
9329 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9330 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9333 option = options.find("tag");
9334 if (option != options.end())
9336 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9337 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9340 else if (type == "tvshows")
9342 if (itemType == "tvshows")
9344 option = options.find("genreid");
9345 if (option != options.end())
9347 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9348 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9351 option = options.find("genre");
9352 if (option != options.end())
9354 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9355 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9358 option = options.find("studioid");
9359 if (option != options.end())
9361 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9362 filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9365 option = options.find("studio");
9366 if (option != options.end())
9368 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9369 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9372 option = options.find("directorid");
9373 if (option != options.end())
9375 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9376 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9379 option = options.find("year");
9380 if (option != options.end())
9381 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9383 option = options.find("actorid");
9384 if (option != options.end())
9386 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9387 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9390 option = options.find("actor");
9391 if (option != options.end())
9393 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9394 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9397 option = options.find("tagid");
9398 if (option != options.end())
9400 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9401 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9404 option = options.find("tag");
9405 if (option != options.end())
9407 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9408 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9411 else if (itemType == "seasons")
9413 option = options.find("genreid");
9414 if (option != options.end())
9416 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9417 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9420 option = options.find("directorid");
9421 if (option != options.end())
9423 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9424 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9427 option = options.find("year");
9428 if (option != options.end())
9429 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9431 option = options.find("actorid");
9432 if (option != options.end())
9434 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9435 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9438 else if (itemType == "episodes")
9441 option = options.find("tvshowid");
9442 if (option != options.end())
9443 idShow = (int)option->second.asInteger();
9446 option = options.find("season");
9447 if (option != options.end())
9448 season = (int)option->second.asInteger();
9450 CStdString strIn = PrepareSQL("= %i", idShow);
9451 GetStackedTvShowList(idShow, strIn);
9455 bool condition = false;
9457 option = options.find("genreid");
9458 if (option != options.end())
9461 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9462 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9465 option = options.find("genre");
9466 if (option != options.end())
9469 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9470 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9473 option = options.find("directorid");
9474 if (option != options.end())
9477 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9478 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9481 option = options.find("director");
9482 if (option != options.end())
9485 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9486 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9489 option = options.find("year");
9490 if (option != options.end())
9493 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9496 option = options.find("actorid");
9497 if (option != options.end())
9500 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9501 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9504 option = options.find("actor");
9505 if (option != options.end())
9508 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9509 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9513 filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9517 if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9518 filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9520 filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9521 VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9526 option = options.find("year");
9527 if (option != options.end())
9528 filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9530 option = options.find("directorid");
9531 if (option != options.end())
9533 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9534 filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9537 option = options.find("director");
9538 if (option != options.end())
9540 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9541 filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9546 else if (type == "musicvideos")
9548 option = options.find("genreid");
9549 if (option != options.end())
9551 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9552 filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9555 option = options.find("genre");
9556 if (option != options.end())
9558 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9559 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9562 option = options.find("studioid");
9563 if (option != options.end())
9565 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9566 filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9569 option = options.find("studio");
9570 if (option != options.end())
9572 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9573 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9576 option = options.find("directorid");
9577 if (option != options.end())
9579 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9580 filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9583 option = options.find("director");
9584 if (option != options.end())
9586 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9587 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9590 option = options.find("year");
9591 if (option != options.end())
9592 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9594 option = options.find("artistid");
9595 if (option != options.end())
9597 if (itemType != "albums")
9598 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9599 filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9602 option = options.find("artist");
9603 if (option != options.end())
9605 if (itemType != "albums")
9607 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9608 filter.AppendJoin(PrepareSQL("join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9610 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9613 option = options.find("albumid");
9614 if (option != options.end())
9615 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()));
9617 option = options.find("tagid");
9618 if (option != options.end())
9620 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9621 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9624 option = options.find("tag");
9625 if (option != options.end())
9627 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9628 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9634 option = options.find("xsp");
9635 if (option != options.end())
9638 if (!xsp.LoadFromJson(option->second.asString()))
9641 // check if the filter playlist matches the item type
9642 if (xsp.GetType() == itemType ||
9643 (xsp.GetGroup() == itemType && !xsp.IsGroupMixed()) ||
9644 // handle episode listings with videodb://tvshows/titles/ which get the rest
9645 // of the path (season and episodeid) appended later
9646 (xsp.GetType() == "episodes" && itemType == "tvshows"))
9648 std::set<CStdString> playlists;
9649 filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9651 if (xsp.GetLimit() > 0)
9652 sorting.limitEnd = xsp.GetLimit();
9653 if (xsp.GetOrder() != SortByNone)
9654 sorting.sortBy = xsp.GetOrder();
9655 if (xsp.GetOrderDirection() != SortOrderNone)
9656 sorting.sortOrder = xsp.GetOrderDirection();
9657 if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
9658 sorting.sortAttributes = SortAttributeIgnoreArticle;
9662 option = options.find("filter");
9663 if (option != options.end())
9665 CSmartPlaylist xspFilter;
9666 if (!xspFilter.LoadFromJson(option->second.asString()))
9669 // check if the filter playlist matches the item type
9670 if (xspFilter.GetType() == itemType)
9672 std::set<CStdString> playlists;
9673 filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9675 // remove the filter if it doesn't match the item type
9677 videoUrl.RemoveOption("filter");