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 // get streamdetails
1913 GetStreamDetails(details);
1915 return !details.IsEmpty();
1919 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1924 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1926 // add all directors
1927 for (unsigned int i = 0; i < details.m_director.size(); i++)
1928 vecDirectors.push_back(AddActor(details.m_director[i],""));
1931 for (unsigned int i = 0; i < details.m_genre.size(); i++)
1932 vecGenres.push_back(AddGenre(details.m_genre[i]));
1934 for (unsigned int i = 0; i < details.m_studio.size(); i++)
1935 vecStudios.push_back(AddStudio(details.m_studio[i]));
1938 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1940 std::vector<std::string> conditions;
1941 for (int i = min + 1; i < max; ++i)
1943 switch (offsets[i].type)
1945 case VIDEODB_TYPE_STRING:
1946 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str()));
1948 case VIDEODB_TYPE_INT:
1949 conditions.push_back(PrepareSQL("c%02d='%i'", i, *(int*)(((char*)&details)+offsets[i].offset)));
1951 case VIDEODB_TYPE_COUNT:
1953 int value = *(int*)(((char*)&details)+offsets[i].offset);
1955 conditions.push_back(PrepareSQL("c%02d=%i", i, value));
1957 conditions.push_back(PrepareSQL("c%02d=NULL", i));
1960 case VIDEODB_TYPE_BOOL:
1961 conditions.push_back(PrepareSQL("c%02d='%s'", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false"));
1963 case VIDEODB_TYPE_FLOAT:
1964 conditions.push_back(PrepareSQL("c%02d='%f'", i, *(float*)(((char*)&details)+offsets[i].offset)));
1966 case VIDEODB_TYPE_STRINGARRAY:
1967 conditions.push_back(PrepareSQL("c%02d='%s'", i, StringUtils::Join(*((std::vector<std::string>*)(((char*)&details)+offsets[i].offset)),
1968 g_advancedSettings.m_videoItemSeparator).c_str()));
1970 case VIDEODB_TYPE_DATE:
1971 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDate().c_str()));
1973 case VIDEODB_TYPE_DATETIME:
1974 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDateTime().c_str()));
1978 return StringUtils::Join(conditions, ",");
1981 //********************************************************************************************************************************
1982 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMovie /* = -1 */)
1989 idMovie = GetMovieId(strFilenameAndPath);
1992 DeleteMovie(strFilenameAndPath, true, idMovie); // true to keep the table entry
1995 // only add a new movie if we don't already have a valid idMovie
1996 // (DeleteMovie is called with bKeepId == true so the movie won't
1997 // be removed from the movie table)
1998 idMovie = AddMovie(strFilenameAndPath);
2001 RollbackTransaction();
2006 vector<int> vecDirectors;
2007 vector<int> vecGenres;
2008 vector<int> vecStudios;
2009 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2011 for (unsigned int i = 0; i < vecGenres.size(); ++i)
2012 AddGenreToMovie(idMovie, vecGenres[i]);
2014 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2015 AddDirectorToMovie(idMovie, vecDirectors[i]);
2017 for (unsigned int i = 0; i < vecStudios.size(); ++i)
2018 AddStudioToMovie(idMovie, vecStudios[i]);
2021 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2022 AddWriterToMovie(idMovie, AddActor(details.m_writingCredits[i],""));
2024 AddCast(idMovie, "movie", "movie", details.m_cast);
2028 if (!details.m_strSet.empty())
2030 idSet = AddSet(details.m_strSet);
2031 // add art if not available
2032 map<string, string> setArt;
2033 if (!GetArtForItem(idSet, "set", setArt))
2034 SetArtForItem(idSet, "set", artwork);
2038 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2040 int idTag = AddTag(details.m_tags[i]);
2041 AddTagToItem(idMovie, idTag, "movie");
2045 for (unsigned int i = 0; i < details.m_country.size(); i++)
2046 AddCountryToMovie(idMovie, AddCountry(details.m_country[i]));
2048 if (details.HasStreamDetails())
2049 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2051 SetArtForItem(idMovie, "movie", artwork);
2053 // query DB for any movies matching imdbid and year
2054 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);
2055 m_pDS->query(strSQL.c_str());
2059 int playCount = m_pDS->fv("files.playCount").get_asInt();
2061 CDateTime lastPlayed;
2062 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2064 int idFile = GetFileId(strFilenameAndPath);
2066 // update with playCount and lastPlayed
2067 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2068 m_pDS->exec(strSQL.c_str());
2073 // update our movie table (we know it was added already above)
2074 // and insert the new row
2075 CStdString sql = "update movie set " + GetValueString(details, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
2077 sql += PrepareSQL(", idSet = %i", idSet);
2079 sql += ", idSet = NULL";
2080 sql += PrepareSQL(" where idMovie=%i", idMovie);
2081 m_pDS->exec(sql.c_str());
2082 CommitTransaction();
2088 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2090 RollbackTransaction();
2094 int CVideoDatabase::SetDetailsForMovieSet(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idSet /* = -1 */)
2096 if (details.m_strTitle.empty())
2104 idSet = AddSet(details.m_strTitle);
2107 RollbackTransaction();
2112 SetArtForItem(idSet, "set", artwork);
2114 // and insert the new row
2115 CStdString sql = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", details.m_strTitle.c_str(), idSet);
2116 m_pDS->exec(sql.c_str());
2117 CommitTransaction();
2123 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
2125 RollbackTransaction();
2129 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, map<string, string> > &seasonArt, int idTvShow /*= -1 */)
2133 if (!m_pDB.get() || !m_pDS.get())
2135 CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
2142 idTvShow = GetTvShowId(strPath);
2145 DeleteDetailsForTvShow(strPath, idTvShow);
2148 idTvShow = AddTvShow(strPath);
2151 RollbackTransaction();
2156 vector<int> vecDirectors;
2157 vector<int> vecGenres;
2158 vector<int> vecStudios;
2159 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2161 AddCast(idTvShow, "tvshow", "show", details.m_cast);
2164 for (i = 0; i < vecGenres.size(); ++i)
2165 AddGenreToTvShow(idTvShow, vecGenres[i]);
2167 for (i = 0; i < vecDirectors.size(); ++i)
2168 AddDirectorToTvShow(idTvShow, vecDirectors[i]);
2170 for (i = 0; i < vecStudios.size(); ++i)
2171 AddStudioToTvShow(idTvShow, vecStudios[i]);
2174 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2176 int idTag = AddTag(details.m_tags[i]);
2177 AddTagToItem(idTvShow, idTag, "tvshow");
2180 // add "all seasons" - the rest are added in SetDetailsForEpisode
2181 AddSeason(idTvShow, -1);
2183 SetArtForItem(idTvShow, "tvshow", artwork);
2184 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
2186 int idSeason = AddSeason(idTvShow, i->first);
2188 SetArtForItem(idSeason, "season", i->second);
2191 // and insert the new row
2192 CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
2193 sql += PrepareSQL(" where idShow=%i", idTvShow);
2194 m_pDS->exec(sql.c_str());
2196 CommitTransaction();
2202 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2204 RollbackTransaction();
2208 int CVideoDatabase::SetDetailsForSeason(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idShow, int idSeason /* = -1 */)
2210 if (idShow < 0 || details.m_iSeason < 0)
2218 idSeason = AddSeason(idShow, details.m_iSeason);
2221 RollbackTransaction();
2226 SetArtForItem(idSeason, "season", artwork);
2228 // and insert the new row
2229 CStdString sql = PrepareSQL("UPDATE seasons SET season=%i WHERE idSeason=%i", details.m_iSeason, idSeason);
2230 m_pDS->exec(sql.c_str());
2231 CommitTransaction();
2237 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
2239 RollbackTransaction();
2243 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idShow, int idEpisode)
2249 idEpisode = GetEpisodeId(strFilenameAndPath);
2252 DeleteEpisode(strFilenameAndPath, idEpisode, true); // true to keep the table entry
2255 // only add a new episode if we don't already have a valid idEpisode
2256 // (DeleteEpisode is called with bKeepId == true so the episode won't
2257 // be removed from the episode table)
2258 idEpisode = AddEpisode(idShow,strFilenameAndPath);
2261 RollbackTransaction();
2266 vector<int> vecDirectors;
2267 vector<int> vecGenres;
2268 vector<int> vecStudios;
2269 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2271 AddCast(idEpisode, "episode", "episode", details.m_cast);
2274 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2275 AddWriterToEpisode(idEpisode, AddActor(details.m_writingCredits[i],""));
2277 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2279 AddDirectorToEpisode(idEpisode, vecDirectors[i]);
2282 if (details.HasStreamDetails())
2284 if (details.m_iFileId != -1)
2285 SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2287 SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
2290 // ensure we have this season already added
2291 AddSeason(idShow, details.m_iSeason);
2293 SetArtForItem(idEpisode, "episode", artwork);
2295 if (details.m_iEpisode != -1 && details.m_iSeason != -1)
2296 { // query DB for any episodes matching idShow, Season and Episode
2297 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);
2298 m_pDS->query(strSQL.c_str());
2302 int playCount = m_pDS->fv("files.playCount").get_asInt();
2304 CDateTime lastPlayed;
2305 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2307 int idFile = GetFileId(strFilenameAndPath);
2309 // update with playCount and lastPlayed
2310 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2311 m_pDS->exec(strSQL.c_str());
2316 // and insert the new row
2317 CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
2318 sql += PrepareSQL(" where idEpisode=%i", idEpisode);
2319 m_pDS->exec(sql.c_str());
2320 CommitTransaction();
2326 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2328 RollbackTransaction();
2332 int CVideoDatabase::GetSeasonId(int showID, int season)
2334 CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
2335 CStdString id = GetSingleValue("seasons", "idSeason", sql);
2338 return strtol(id.c_str(), NULL, 10);
2341 int CVideoDatabase::AddSeason(int showID, int season)
2343 int seasonId = GetSeasonId(showID, season);
2346 if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
2347 seasonId = (int)m_pDS->lastinsertid();
2352 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
2359 idMVideo = GetMusicVideoId(strFilenameAndPath);
2362 DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
2365 // only add a new musicvideo if we don't already have a valid idMVideo
2366 // (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
2367 // be removed from the musicvideo table)
2368 idMVideo = AddMusicVideo(strFilenameAndPath);
2371 RollbackTransaction();
2376 vector<int> vecDirectors;
2377 vector<int> vecGenres;
2378 vector<int> vecStudios;
2379 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2382 if (!details.m_artist.empty())
2384 for (unsigned int i = 0; i < details.m_artist.size(); i++)
2386 CStdString artist = details.m_artist[i];
2387 StringUtils::Trim(artist);
2388 int idArtist = AddActor(artist,"");
2389 AddArtistToMusicVideo(idMVideo, idArtist);
2394 for (i = 0; i < vecGenres.size(); ++i)
2396 AddGenreToMusicVideo(idMVideo, vecGenres[i]);
2399 for (i = 0; i < vecDirectors.size(); ++i)
2401 AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
2404 for (i = 0; i < vecStudios.size(); ++i)
2406 AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2410 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2412 int idTag = AddTag(details.m_tags[i]);
2413 AddTagToItem(idMVideo, idTag, "musicvideo");
2416 if (details.HasStreamDetails())
2417 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2419 SetArtForItem(idMVideo, "musicvideo", artwork);
2421 // update our movie table (we know it was added already above)
2422 // and insert the new row
2423 CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
2424 sql += PrepareSQL(" where idMVideo=%i", idMVideo);
2425 m_pDS->exec(sql.c_str());
2426 CommitTransaction();
2432 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2434 RollbackTransaction();
2438 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2440 // AddFile checks to make sure the file isn't already in the DB first
2441 int idFile = AddFile(strFileNameAndPath);
2444 SetStreamDetailsForFileId(details, idFile);
2447 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2455 m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2457 for (int i=1; i<=details.GetVideoStreamCount(); i++)
2459 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2460 "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration, strStereoMode) "
2461 "VALUES (%i,%i,'%s',%f,%i,%i,%i,'%s')",
2462 idFile, (int)CStreamDetail::VIDEO,
2463 details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2464 details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i),
2465 details.GetStereoMode(i).c_str()));
2467 for (int i=1; i<=details.GetAudioStreamCount(); i++)
2469 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2470 "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2471 "VALUES (%i,%i,'%s',%i,'%s')",
2472 idFile, (int)CStreamDetail::AUDIO,
2473 details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2474 details.GetAudioLanguage(i).c_str()));
2476 for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2478 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2479 "(idFile, iStreamType, strSubtitleLanguage) "
2480 "VALUES (%i,%i,'%s')",
2481 idFile, (int)CStreamDetail::SUBTITLE,
2482 details.GetSubtitleLanguage(i).c_str()));
2485 // update the runtime information, if empty
2486 if (details.GetVideoDuration())
2488 vector< pair<string, int> > tables;
2489 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
2490 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
2491 tables.push_back(make_pair("musicvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
2492 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
2494 CStdString sql = PrepareSQL("update %s set c%02d=%d where idFile=%d and c%02d=''",
2495 i->first.c_str(), i->second, details.GetVideoDuration(), idFile, i->second);
2500 CommitTransaction();
2504 RollbackTransaction();
2505 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2509 //********************************************************************************************************************************
2510 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2514 if (NULL == m_pDB.get()) return ;
2515 if (NULL == m_pDS.get()) return ;
2517 if (idMovie < 0) return ;
2520 if (iType == VIDEODB_CONTENT_MOVIES)
2521 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 );
2522 if (iType == VIDEODB_CONTENT_EPISODES)
2523 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 );
2524 if (iType == VIDEODB_CONTENT_TVSHOWS)
2525 strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2526 if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2527 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 );
2529 m_pDS->query( strSQL.c_str() );
2532 if (iType != VIDEODB_CONTENT_TVSHOWS)
2534 CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2535 ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2538 filePath = m_pDS->fv("path.strPath").get_asString();
2544 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2548 //********************************************************************************************************************************
2549 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend, long partNumber)
2553 if (URIUtils::IsStack(strFilenameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(strFilenameAndPath),false).IsDVDImage())
2555 CStackDirectory dir;
2556 CFileItemList fileList;
2557 dir.GetDirectory(strFilenameAndPath, fileList);
2560 for (int i = fileList.Size() - 1; i >= 0; i--) // put the bookmarks of the highest part first in the list
2561 GetBookMarksForFile(fileList[i]->GetPath(), bookmarks, type, true, (i+1));
2565 int idFile = GetFileId(strFilenameAndPath);
2566 if (idFile < 0) return ;
2568 bookmarks.erase(bookmarks.begin(), bookmarks.end());
2569 if (NULL == m_pDB.get()) return ;
2570 if (NULL == m_pDS.get()) return ;
2572 CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2573 m_pDS->query( strSQL.c_str() );
2574 while (!m_pDS->eof())
2577 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2578 bookmark.partNumber = partNumber;
2579 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2580 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2581 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2582 bookmark.player = m_pDS->fv("player").get_asString();
2583 bookmark.type = type;
2584 if (type == CBookmark::EPISODE)
2586 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);
2587 m_pDS2->query(strSQL2.c_str());
2588 bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2589 bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2592 bookmarks.push_back(bookmark);
2595 //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2601 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2605 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2607 VECBOOKMARKS bookmarks;
2608 GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2609 if (bookmarks.size() > 0)
2611 bookmark = bookmarks[0];
2617 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2619 if (!m_pDB.get() || !m_pDS.get())
2622 int fileID = GetFileId(strFilenameAndPath);
2628 CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2629 m_pDS->exec(sql.c_str());
2633 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2637 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2641 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);
2642 m_pDS->query(strSQL.c_str());
2643 while (!m_pDS->eof())
2645 episodes.push_back(GetDetailsForEpisode(m_pDS));
2652 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2656 //********************************************************************************************************************************
2657 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2661 int idFile = AddFile(strFilenameAndPath);
2664 if (NULL == m_pDB.get()) return ;
2665 if (NULL == m_pDS.get()) return ;
2669 if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2671 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2673 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
2675 /* get a bookmark within the same time as previous */
2676 double mintime = bookmark.timeInSeconds - 0.5f;
2677 double maxtime = bookmark.timeInSeconds + 0.5f;
2678 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());
2681 if (type != CBookmark::EPISODE)
2684 m_pDS->query( strSQL.c_str() );
2685 if (m_pDS->num_rows() != 0)
2686 idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2689 // update or insert depending if it existed before
2690 if (idBookmark >= 0 )
2691 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);
2693 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);
2695 m_pDS->exec(strSQL.c_str());
2699 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2703 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2707 int idFile = GetFileId(strFilenameAndPath);
2708 if (idFile < 0) return ;
2709 if (NULL == m_pDB.get()) return ;
2710 if (NULL == m_pDS.get()) return ;
2712 /* a litle bit uggly, we clear first bookmark that is within one second of given */
2713 /* should be no problem since we never add bookmarks that are closer than that */
2714 double mintime = bookmark.timeInSeconds - 0.5f;
2715 double maxtime = bookmark.timeInSeconds + 0.5f;
2716 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);
2718 m_pDS->query( strSQL.c_str() );
2719 if (m_pDS->num_rows() != 0)
2721 int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2722 strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2723 m_pDS->exec(strSQL.c_str());
2724 if (type == CBookmark::EPISODE)
2726 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);
2727 m_pDS->exec(strSQL.c_str());
2735 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2739 //********************************************************************************************************************************
2740 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2744 int idFile = GetFileId(strFilenameAndPath);
2745 if (idFile < 0) return ;
2746 if (NULL == m_pDB.get()) return ;
2747 if (NULL == m_pDS.get()) return ;
2749 CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2750 m_pDS->exec(strSQL.c_str());
2751 if (type == CBookmark::EPISODE)
2753 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2754 m_pDS->exec(strSQL.c_str());
2759 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2764 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2768 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);
2769 m_pDS->query( strSQL.c_str() );
2772 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2773 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2774 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2775 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2776 bookmark.player = m_pDS->fv("player").get_asString();
2777 bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2788 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2794 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2798 int idFile = GetFileId(tag.m_strFileNameAndPath);
2799 // delete the current episode for the selected episode number
2800 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);
2801 m_pDS->exec(strSQL.c_str());
2803 AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2804 int idBookmark = (int)m_pDS->lastinsertid();
2805 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);
2806 m_pDS->exec(strSQL.c_str());
2810 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2814 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2818 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2819 m_pDS->exec(strSQL.c_str());
2820 strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2821 m_pDS->exec(strSQL.c_str());
2825 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2829 //********************************************************************************************************************************
2830 void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
2836 GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
2838 DeleteMovie(path, bKeepId, idMovie);
2841 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
2845 if (NULL == m_pDB.get()) return ;
2846 if (NULL == m_pDS.get()) return ;
2849 idMovie = GetMovieId(strFilenameAndPath);
2857 strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2858 m_pDS->exec(strSQL.c_str());
2860 strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2861 m_pDS->exec(strSQL.c_str());
2863 strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2864 m_pDS->exec(strSQL.c_str());
2866 strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2867 m_pDS->exec(strSQL.c_str());
2869 strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2870 m_pDS->exec(strSQL.c_str());
2872 strSQL=PrepareSQL("delete from writerlinkmovie where idMovie=%i", idMovie);
2873 m_pDS->exec(strSQL.c_str());
2875 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2877 // keep the movie table entry, linking to tv shows, and bookmarks
2878 // so we can update the data in place
2879 // the ancilliary tables are still purged
2882 ClearBookMarksOfFile(strFilenameAndPath);
2884 strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2885 m_pDS->exec(strSQL.c_str());
2887 strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2888 m_pDS->exec(strSQL.c_str());
2890 CStdString strPath, strFileName;
2891 SplitPath(strFilenameAndPath,strPath,strFileName);
2892 InvalidatePathHash(strPath);
2895 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2897 AnnounceRemove("movie", idMovie);
2899 CommitTransaction();
2904 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2905 RollbackTransaction();
2909 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2915 GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2917 DeleteTvShow(path, bKeepId, idTvShow);
2920 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2924 if (NULL == m_pDB.get()) return ;
2925 if (NULL == m_pDS.get()) return ;
2928 idTvShow = GetTvShowId(strPath);
2935 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);
2936 m_pDS2->query(strSQL.c_str());
2937 while (!m_pDS2->eof())
2939 CStdString strFilenameAndPath;
2940 CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2941 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2942 ConstructPath(strFilenameAndPath, strPath, strFileName);
2943 DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2947 DeleteDetailsForTvShow(strPath, idTvShow);
2949 strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2950 m_pDS->exec(strSQL.c_str());
2952 // keep tvshow table and movielink table so we can update data in place
2955 strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2956 m_pDS->exec(strSQL.c_str());
2958 strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2959 m_pDS->exec(strSQL.c_str());
2961 strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2962 m_pDS->exec(strSQL.c_str());
2964 InvalidatePathHash(strPath);
2967 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2969 AnnounceRemove("tvshow", idTvShow);
2971 CommitTransaction();
2976 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2977 RollbackTransaction();
2981 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2987 GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2989 DeleteEpisode(path, idEpisode, bKeepId);
2992 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
2996 if (NULL == m_pDB.get()) return ;
2997 if (NULL == m_pDS.get()) return ;
3000 idEpisode = GetEpisodeId(strFilenameAndPath);
3007 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3009 AnnounceRemove("episode", idEpisode);
3012 strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
3013 m_pDS->exec(strSQL.c_str());
3015 strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
3016 m_pDS->exec(strSQL.c_str());
3018 strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
3019 m_pDS->exec(strSQL.c_str());
3021 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3023 // keep episode table entry and bookmarks so we can update the data in place
3024 // the ancilliary tables are still purged
3027 ClearBookMarksOfFile(strFilenameAndPath);
3029 strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
3030 m_pDS->exec(strSQL.c_str());
3036 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3040 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
3042 if (idMusicVideo < 0)
3046 GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
3048 DeleteMusicVideo(path, bKeepId, idMusicVideo);
3051 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
3055 if (NULL == m_pDB.get()) return ;
3056 if (NULL == m_pDS.get()) return ;
3059 idMVideo = GetMusicVideoId(strFilenameAndPath);
3067 strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
3068 m_pDS->exec(strSQL.c_str());
3070 strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
3071 m_pDS->exec(strSQL.c_str());
3073 strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
3074 m_pDS->exec(strSQL.c_str());
3076 strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
3077 m_pDS->exec(strSQL.c_str());
3079 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3081 // keep the music video table entry and bookmarks so we can update data in place
3082 // the ancilliary tables are still purged
3085 ClearBookMarksOfFile(strFilenameAndPath);
3087 strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
3088 m_pDS->exec(strSQL.c_str());
3090 CStdString strPath, strFileName;
3091 SplitPath(strFilenameAndPath,strPath,strFileName);
3092 InvalidatePathHash(strPath);
3095 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3097 AnnounceRemove("musicvideo", idMVideo);
3099 CommitTransaction();
3104 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3105 RollbackTransaction();
3109 void CVideoDatabase::DeleteStreamDetails(int idFile)
3111 m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
3114 void CVideoDatabase::DeleteSet(int idSet)
3118 if (NULL == m_pDB.get()) return ;
3119 if (NULL == m_pDS.get()) return ;
3122 strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
3123 m_pDS->exec(strSQL.c_str());
3124 strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3125 m_pDS->exec(strSQL.c_str());
3129 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3133 void CVideoDatabase::ClearMovieSet(int idMovie)
3135 SetMovieSet(idMovie, -1);
3138 void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
3141 ExecuteQuery(PrepareSQL("update movie set idSet = %i where idMovie = %i", idSet, idMovie));
3143 ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
3146 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3150 if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3154 if (mediaType == VIDEODB_CONTENT_MOVIES)
3156 else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3158 else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3159 type = "musicvideo";
3164 strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3165 m_pDS->exec(strSQL.c_str());
3169 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3173 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3175 GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3178 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3180 for (int i = min + 1; i < max; i++)
3182 switch (offsets[i].type)
3184 case VIDEODB_TYPE_STRING:
3185 *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3187 case VIDEODB_TYPE_INT:
3188 case VIDEODB_TYPE_COUNT:
3189 *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3191 case VIDEODB_TYPE_BOOL:
3192 *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3194 case VIDEODB_TYPE_FLOAT:
3195 *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3197 case VIDEODB_TYPE_STRINGARRAY:
3198 *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3200 case VIDEODB_TYPE_DATE:
3201 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3203 case VIDEODB_TYPE_DATETIME:
3204 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3210 DWORD movieTime = 0;
3213 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3215 CVideoInfoTag details;
3220 case VIDEODB_CONTENT_MOVIES:
3221 GetMovieInfo("", details, id);
3223 case VIDEODB_CONTENT_TVSHOWS:
3224 GetTvShowInfo("", details, id);
3226 case VIDEODB_CONTENT_EPISODES:
3227 GetEpisodeInfo("", details, id);
3229 case VIDEODB_CONTENT_MUSICVIDEOS:
3230 GetMusicVideoInfo("", details, id);
3239 bool CVideoDatabase::GetStreamDetails(CFileItem& item)
3241 // Note that this function (possibly) creates VideoInfoTags for items that don't have one yet!
3244 if (item.HasVideoInfoTag())
3245 fileId = item.GetVideoInfoTag()->m_iFileId;
3248 fileId = GetFileId(item);
3253 // Have a file id, get stream details if available (creates tag either way)
3254 item.GetVideoInfoTag()->m_iFileId = fileId;
3255 return GetStreamDetails(*item.GetVideoInfoTag());
3258 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3260 if (tag.m_iFileId < 0)
3263 bool retVal = false;
3265 CStreamDetails& details = tag.m_streamDetails;
3268 auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3271 CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3276 CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3279 case CStreamDetail::VIDEO:
3281 CStreamDetailVideo *p = new CStreamDetailVideo();
3282 p->m_strCodec = pDS->fv(2).get_asString();
3283 p->m_fAspect = pDS->fv(3).get_asFloat();
3284 p->m_iWidth = pDS->fv(4).get_asInt();
3285 p->m_iHeight = pDS->fv(5).get_asInt();
3286 p->m_iDuration = pDS->fv(10).get_asInt();
3287 p->m_strStereoMode = pDS->fv(11).get_asString();
3288 details.AddStream(p);
3292 case CStreamDetail::AUDIO:
3294 CStreamDetailAudio *p = new CStreamDetailAudio();
3295 p->m_strCodec = pDS->fv(6).get_asString();
3296 if (pDS->fv(7).get_isNull())
3297 p->m_iChannels = -1;
3299 p->m_iChannels = pDS->fv(7).get_asInt();
3300 p->m_strLanguage = pDS->fv(8).get_asString();
3301 details.AddStream(p);
3305 case CStreamDetail::SUBTITLE:
3307 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3308 p->m_strLanguage = pDS->fv(9).get_asString();
3309 details.AddStream(p);
3322 CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3324 details.DetermineBestStreams();
3326 if (details.GetVideoDuration() > 0)
3327 tag.m_duration = details.GetVideoDuration();
3332 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3334 if (tag.m_iFileId < 0)
3341 if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3343 CStackDirectory dir;
3344 CFileItemList fileList;
3345 dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3346 tag.m_resumePoint.Reset();
3347 for (int i = fileList.Size() - 1; i >= 0; i--)
3350 if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3352 tag.m_resumePoint = bookmark;
3353 tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3361 CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3362 m_pDS2->query( strSQL.c_str() );
3365 tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3366 tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3367 tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3368 tag.m_resumePoint.type = CBookmark::RESUME;
3376 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3382 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3384 return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3387 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3389 CVideoInfoTag details;
3394 DWORD time = XbmcThreads::SystemClockMillis();
3395 int idMovie = record->at(0).get_asInt();
3397 GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3399 details.m_iDbId = idMovie;
3400 details.m_type = "movie";
3402 details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3403 details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3404 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3405 details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3406 CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3407 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3408 details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3409 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3410 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3411 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3412 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3413 details.m_resumePoint.type = CBookmark::RESUME;
3415 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3419 GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3421 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3422 details.m_strPictureURL.Parse();
3425 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);
3426 m_pDS2->query(strSQL.c_str());
3427 while (!m_pDS2->eof())
3429 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3433 // create tvshowlink string
3435 GetLinksToTvShow(idMovie,links);
3436 for (unsigned int i=0;i<links.size();++i)
3438 CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3439 VIDEODB_ID_TV_TITLE,links[i]);
3440 m_pDS2->query(strSQL.c_str());
3442 details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3446 // get streamdetails
3447 GetStreamDetails(details);
3452 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3454 return GetDetailsForTvShow(pDS->get_sql_record(), getDetails, item);
3457 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3459 CVideoInfoTag details;
3464 DWORD time = XbmcThreads::SystemClockMillis();
3465 int idTvShow = record->at(0).get_asInt();
3467 GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3468 details.m_iDbId = idTvShow;
3469 details.m_type = "tvshow";
3470 details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3471 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3472 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3473 details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3474 details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3475 details.m_strShowPath = details.m_strPath;
3476 details.m_strShowTitle = details.m_strTitle;
3477 if (details.m_premiered.IsValid())
3478 details.m_iYear = details.m_premiered.GetYear();
3480 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3484 GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3487 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);
3488 m_pDS2->query(strSQL.c_str());
3489 while (!m_pDS2->eof())
3491 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3495 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3496 details.m_strPictureURL.Parse();
3501 item->m_dateTime = details.m_premiered;
3502 item->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
3503 item->SetProperty("totalepisodes", details.m_iEpisode);
3504 item->SetProperty("numepisodes", details.m_iEpisode); // will be changed later to reflect watchmode setting
3505 item->SetProperty("watchedepisodes", details.m_playCount);
3506 item->SetProperty("unwatchedepisodes", details.m_iEpisode - details.m_playCount);
3508 details.m_playCount = (details.m_iEpisode <= details.m_playCount) ? 1 : 0;
3513 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3515 return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3518 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3520 CVideoInfoTag details;
3525 DWORD time = XbmcThreads::SystemClockMillis();
3526 int idEpisode = record->at(0).get_asInt();
3528 GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3529 details.m_iDbId = idEpisode;
3530 details.m_type = "episode";
3531 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3532 details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3533 CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3534 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3535 details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3536 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3537 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3538 details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3539 details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3540 details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3541 details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3542 details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3543 details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3544 details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3546 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3547 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3548 details.m_resumePoint.type = CBookmark::RESUME;
3550 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3554 GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3555 GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3557 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3558 details.m_strPictureURL.Parse();
3559 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);
3560 m_pDS2->query(strSQL.c_str());
3562 details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3565 // get streamdetails
3566 GetStreamDetails(details);
3571 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3573 return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3576 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3578 CVideoInfoTag details;
3580 unsigned int time = XbmcThreads::SystemClockMillis();
3581 int idMVideo = record->at(0).get_asInt();
3583 GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3584 details.m_iDbId = idMVideo;
3585 details.m_type = "musicvideo";
3587 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3588 details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3589 CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3590 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3591 details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3592 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3593 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3594 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3595 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3596 details.m_resumePoint.type = CBookmark::RESUME;
3598 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3603 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);
3604 m_pDS2->query(strSQL.c_str());
3605 while (!m_pDS2->eof())
3607 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3612 details.m_strPictureURL.Parse();
3614 // get streamdetails
3615 GetStreamDetails(details);
3620 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3624 if (!m_pDB.get()) return;
3625 if (!m_pDS2.get()) return;
3627 CStdString sql = PrepareSQL("SELECT actors.strActor,"
3628 " actorlink%s.strRole,"
3629 " actorlink%s.iOrder,"
3634 " actorlink%s.idActor=actors.idActor"
3636 " art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3637 "WHERE actorlink%s.%s=%i "
3638 "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());
3639 m_pDS2->query(sql.c_str());
3640 while (!m_pDS2->eof())
3643 info.strName = m_pDS2->fv(0).get_asString();
3645 for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3647 if (i->strName == info.strName)
3655 info.strRole = m_pDS2->fv(1).get_asString();
3656 info.order = m_pDS2->fv(2).get_asInt();
3657 info.thumbUrl.ParseString(m_pDS2->fv(3).get_asString());
3658 info.thumb = m_pDS2->fv(4).get_asString();
3659 cast.push_back(info);
3667 CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3671 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3672 /// \retval Returns true if the settings exist, false otherwise.
3673 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3677 // obtain the FileID (if it exists)
3678 #ifdef NEW_VIDEODB_METHODS
3679 if (NULL == m_pDB.get()) return false;
3680 if (NULL == m_pDS.get()) return false;
3681 CStdString strPath, strFileName;
3682 URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3683 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());
3685 int idFile = GetFileId(strFilenameAndPath);
3686 if (idFile < 0) return false;
3687 if (NULL == m_pDB.get()) return false;
3688 if (NULL == m_pDS.get()) return false;
3689 // ok, now obtain the settings for this file
3690 CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3692 m_pDS->query( strSQL.c_str() );
3693 if (m_pDS->num_rows() > 0)
3694 { // get the video settings info
3695 settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3696 settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3697 settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3698 settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3699 settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3700 settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3701 settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3702 settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3703 settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3704 settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3705 settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3706 settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3707 settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3708 settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3709 settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3710 settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3711 settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3712 settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3713 settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3714 settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3715 settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3716 settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3717 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3718 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3719 settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3720 settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3721 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3722 settings.m_StereoMode = m_pDS->fv("StereoMode").get_asInt();
3723 settings.m_StereoInvert = m_pDS->fv("StereoInvert").get_asBool();
3724 settings.m_SubtitleCached = false;
3732 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3737 /// \brief Sets the settings for a particular video file
3738 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3742 if (NULL == m_pDB.get()) return ;
3743 if (NULL == m_pDS.get()) return ;
3744 int idFile = AddFile(strFilenameAndPath);
3747 CStdString strSQL = StringUtils::Format("select * from settings where idFile=%i", idFile);
3748 m_pDS->query( strSQL.c_str() );
3749 if (m_pDS->num_rows() > 0)
3753 strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3754 "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3755 "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3756 "DeinterlaceMode=%i,",
3757 setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3758 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3759 setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3760 setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3761 setting.m_DeinterlaceMode);
3763 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);
3765 m_pDS->exec(strSQL.c_str());
3771 strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3772 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3773 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3774 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3775 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode,StereoMode,StereoInvert) "
3777 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)",
3778 idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3779 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3780 setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3781 setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3782 setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3783 setting.m_DeinterlaceMode, setting.m_StereoMode, setting.m_StereoInvert);
3784 m_pDS->exec(strSQL.c_str());
3789 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3793 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3795 for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3796 SetArtForItem(mediaId, mediaType, i->first, i->second);
3799 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3803 if (NULL == m_pDB.get()) return;
3804 if (NULL == m_pDS.get()) return;
3806 // don't set <foo>.<bar> art types - these are derivative types from parent items
3807 if (artType.find('.') != string::npos)
3810 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());
3811 m_pDS->query(sql.c_str());
3814 int artId = m_pDS->fv(0).get_asInt();
3816 sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3817 m_pDS->exec(sql.c_str());
3822 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());
3823 m_pDS->exec(sql.c_str());
3828 CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3832 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3836 if (NULL == m_pDB.get()) return false;
3837 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3839 CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3840 m_pDS2->query(sql.c_str());
3841 while (!m_pDS2->eof())
3843 art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3847 return !art.empty();
3851 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3856 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3858 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());
3859 return GetSingleValue(query, m_pDS2);
3862 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::string &artType)
3864 return ExecuteQuery(PrepareSQL("DELETE FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str()));
3867 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::set<std::string> &artTypes)
3870 for (set<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
3871 result &= RemoveArtForItem(mediaId, mediaType, *i);
3876 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3880 if (NULL == m_pDB.get()) return false;
3881 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3883 // get all seasons for this show
3884 CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3885 m_pDS2->query(sql.c_str());
3887 vector< pair<int, int> > seasons;
3888 while (!m_pDS2->eof())
3890 seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3895 for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3897 map<string, string> art;
3898 GetArtForItem(i->first, "season", art);
3899 seasonArt.insert(make_pair(i->second,art));
3905 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3910 bool CVideoDatabase::GetArtTypes(const std::string &mediaType, std::vector<std::string> &artTypes)
3914 if (NULL == m_pDB.get()) return false;
3915 if (NULL == m_pDS.get()) return false;
3917 CStdString sql = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str());
3918 int numRows = RunQuery(sql);
3920 return numRows == 0;
3922 while (!m_pDS->eof())
3924 artTypes.push_back(m_pDS->fv(0).get_asString());
3932 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str());
3937 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3938 /// \retval Returns true if the stack times exist, false otherwise.
3939 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> ×)
3943 // obtain the FileID (if it exists)
3944 int idFile = GetFileId(filePath);
3945 if (idFile < 0) return false;
3946 if (NULL == m_pDB.get()) return false;
3947 if (NULL == m_pDS.get()) return false;
3948 // ok, now obtain the settings for this file
3949 CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3950 m_pDS->query( strSQL.c_str() );
3951 if (m_pDS->num_rows() > 0)
3952 { // get the video settings info
3953 CStdStringArray timeString;
3955 StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3957 for (unsigned int i = 0; i < timeString.size(); i++)
3959 times.push_back(atoi(timeString[i].c_str()));
3960 timeTotal += atoi(timeString[i].c_str());
3963 return (timeTotal > 0);
3969 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3974 /// \brief Sets the stack times for a particular video file
3975 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> ×)
3979 if (NULL == m_pDB.get()) return ;
3980 if (NULL == m_pDS.get()) return ;
3981 int idFile = AddFile(filePath);
3985 // delete any existing items
3986 m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3989 CStdString timeString = StringUtils::Format("%i", times[0]);
3990 for (unsigned int i = 1; i < times.size(); i++)
3991 timeString += StringUtils::Format(",%i", times[i]);
3993 m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3997 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4001 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
4003 if(URIUtils::IsMultiPath(strPath))
4005 vector<CStdString> paths;
4006 CMultiPathDirectory::GetPaths(strPath, paths);
4008 for(unsigned i=0;i<paths.size();i++)
4009 RemoveContentForPath(paths[i], progress);
4014 if (NULL == m_pDB.get()) return ;
4015 if (NULL == m_pDS.get()) return ;
4019 progress->SetHeading(700);
4020 progress->SetLine(0, "");
4021 progress->SetLine(1, 313);
4022 progress->SetLine(2, 330);
4023 progress->SetPercentage(0);
4024 progress->StartModal();
4025 progress->ShowProgressBar(true);
4027 vector< pair<int,string> > paths;
4028 GetSubPaths(strPath, paths);
4030 for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
4032 bool bMvidsChecked=false;
4035 progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
4036 progress->Progress();
4039 if (HasTvShowInfo(i->second))
4040 DeleteTvShow(i->second);
4043 CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
4044 m_pDS2->query(strSQL.c_str());
4047 strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4048 m_pDS2->query(strSQL.c_str());
4049 bMvidsChecked = true;
4051 while (!m_pDS2->eof())
4053 CStdString strMoviePath;
4054 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
4055 ConstructPath(strMoviePath, i->second, strFileName);
4056 if (HasMovieInfo(strMoviePath))
4057 DeleteMovie(strMoviePath);
4058 if (HasMusicVideoInfo(strMoviePath))
4059 DeleteMusicVideo(strMoviePath);
4061 if (m_pDS2->eof() && !bMvidsChecked)
4063 strSQL =PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4064 m_pDS2->query(strSQL.c_str());
4065 bMvidsChecked = true;
4069 m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
4075 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
4081 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
4083 // if we have a multipath, set scraper for all contained paths too
4084 if(URIUtils::IsMultiPath(filePath))
4086 vector<CStdString> paths;
4087 CMultiPathDirectory::GetPaths(filePath, paths);
4089 for(unsigned i=0;i<paths.size();i++)
4090 SetScraperForPath(paths[i],scraper,settings);
4095 if (NULL == m_pDB.get()) return ;
4096 if (NULL == m_pDS.get()) return ;
4098 int idPath = AddPath(filePath);
4104 if (settings.exclude)
4105 { //NB See note in ::GetScraperForPath about strContent=='none'
4106 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
4109 { // catch clearing content, but not excluding
4110 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
4114 CStdString content = TranslateContent(scraper->Content());
4115 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);
4117 m_pDS->exec(strSQL.c_str());
4121 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4125 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
4129 if (NULL == m_pDB.get()) return false;
4130 if (NULL == m_pDS.get()) return false;
4132 CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
4133 if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
4135 bool found = m_pDS->fv(0).get_asInt() > 0;
4141 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
4149 CArtItem() { art_id = 0; media_id = 0; };
4157 void CVideoDatabase::UpdateTables(int iVersion)
4161 m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4162 m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4165 { // add seasons table
4166 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4167 // insert all seasons for each show
4168 m_pDS->query("SELECT idShow FROM tvshow");
4169 while (!m_pDS->eof())
4171 CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4176 " JOIN tvshowlinkepisode ON"
4177 " episode.idEpisode=tvshowlinkepisode.idEpisode"
4178 " WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4179 m_pDS2->exec(sql.c_str());
4180 // and the "all seasons node"
4181 sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4182 m_pDS2->exec(sql.c_str());
4188 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4190 CMediaSettings::Get().SetVideoNeedsUpdate(63);
4191 CSettings::Get().Save();
4194 { // add idShow to episode table
4195 m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4196 m_pDS->query("SELECT idEpisode FROM episode");
4197 while (!m_pDS->eof())
4199 int idEpisode = m_pDS->fv(0).get_asInt();
4200 CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4201 m_pDS2->exec(update.c_str());
4204 m_pDS->exec("DROP TABLE tvshowlinkepisode");
4208 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4209 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4212 { // add idSet to movie table
4213 m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4214 m_pDS->query("SELECT idMovie FROM movie");
4215 while (!m_pDS->eof())
4217 int idMovie = m_pDS->fv(0).get_asInt();
4218 CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4219 m_pDS2->exec(sql.c_str());
4222 m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4225 { // update old art URLs
4226 m_pDS->query("select art_id,url from art where url like 'image://%%'");
4227 vector< pair<int, string> > art;
4228 while (!m_pDS->eof())
4230 art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4234 for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4235 m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4238 { // update URL encoded paths
4239 m_pDS->query("select idFile, strFilename from files");
4240 vector< pair<int, string> > files;
4241 while (!m_pDS->eof())
4243 files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4248 for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4250 std::string filename = i->second;
4251 bool update = URIUtils::UpdateUrlEncoding(filename) &&
4252 (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4256 m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4260 { // Update thumb to poster or banner as applicable
4261 CTextureDatabase db;
4264 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')");
4265 vector<CArtItem> art;
4266 while (!m_pDS->eof())
4268 CTextureDetails details;
4269 if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4272 item.art_id = m_pDS->fv(0).get_asInt();
4273 item.art_url = m_pDS->fv(1).get_asString();
4274 item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4275 item.media_id = m_pDS->fv(2).get_asInt();
4276 item.media_type = m_pDS->fv(3).get_asString();
4277 if (item.art_type != "thumb")
4278 art.push_back(item);
4283 for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4285 if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4286 m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4288 m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4293 { // update the runtime columns
4294 vector< pair<string, int> > tables;
4295 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4296 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4297 tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4298 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4300 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);
4301 m_pDS->query(sql.c_str());
4302 vector< pair<int, int> > videos;
4303 while (!m_pDS->eof())
4305 int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4307 videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4311 for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4312 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));
4317 m_pDS->exec("ALTER TABLE settings ADD StereoMode integer");
4318 m_pDS->exec("ALTER TABLE settings ADD StereoInvert bool");
4321 m_pDS->exec("ALTER TABLE streamdetails ADD strStereoMode text");
4324 int CVideoDatabase::GetSchemaVersion() const
4329 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4331 SScanSettings settings;
4332 bool foundDirectly = false;
4333 ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4334 if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4335 return false; // episodes
4336 return settings.parent_name_root; // shows, movies, musicvids
4339 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4341 if(URIUtils::IsMultiPath(strPath))
4343 vector<CStdString> paths;
4344 CMultiPathDirectory::GetPaths(strPath, paths);
4347 for(unsigned i=0;i<paths.size();i++)
4348 ret |= GetPlayCounts(paths[i], items);
4353 if (URIUtils::IsPlugin(strPath))
4356 pathID = GetPathId(url.GetWithoutFilename());
4359 pathID = GetPathId(strPath);
4361 return false; // path (and thus files) aren't in the database
4366 if (NULL == m_pDB.get()) return false;
4367 if (NULL == m_pDS.get()) return false;
4369 // TODO: also test a single query for the above and below
4370 CStdString sql = PrepareSQL(
4372 " files.strFilename, files.playCount,"
4373 " bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4375 " LEFT JOIN bookmark ON"
4376 " files.idFile = bookmark.idFile AND bookmark.type = %i"
4377 " WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4379 if (RunQuery(sql) <= 0)
4382 items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4383 while (!m_pDS->eof())
4386 ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4387 CFileItemPtr item = items.Get(path);
4390 item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4391 if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4393 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4394 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4395 item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4404 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4409 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4411 int id = GetFileId(item);
4413 return 0; // not in db, so not watched
4418 if (NULL == m_pDB.get()) return -1;
4419 if (NULL == m_pDS.get()) return -1;
4421 CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4423 if (m_pDS->query(strSQL.c_str()))
4425 // there should only ever be one row returned
4426 if (m_pDS->num_rows() == 1)
4427 count = m_pDS->fv(0).get_asInt();
4434 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4439 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4441 if (NULL == m_pDB.get()) return;
4442 if (NULL == m_pDS.get()) return;
4443 if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4446 if (type == VIDEODB_CONTENT_TVSHOWS)
4447 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);
4448 else if (type == VIDEODB_CONTENT_MOVIES)
4449 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);
4453 m_pDS->exec(exec.c_str());
4455 if (type == VIDEODB_CONTENT_TVSHOWS)
4456 AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4457 else if (type == VIDEODB_CONTENT_MOVIES)
4458 AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4462 CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4466 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4469 if (item.HasProperty("original_listitem_url") &&
4470 URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4472 CFileItem item2(item);
4473 item2.SetPath(item.GetProperty("original_listitem_url").asString());
4474 id = AddFile(item2);
4481 // and mark as watched
4484 if (NULL == m_pDB.get()) return ;
4485 if (NULL == m_pDS.get()) return ;
4490 if (!date.IsValid())
4491 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4493 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4497 if (!date.IsValid())
4498 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4500 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4503 m_pDS->exec(strSQL.c_str());
4505 // We only need to announce changes to video items in the library
4506 if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4508 // Only provide the "playcount" value if it has actually changed
4509 if (item.GetVideoInfoTag()->m_playCount != count)
4512 data["playcount"] = count;
4513 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4516 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4521 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4525 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4527 SetPlayCount(item, GetPlayCount(item) + 1);
4530 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4532 SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4535 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4539 if (NULL == m_pDB.get()) return ;
4540 if (NULL == m_pDS.get()) return ;
4542 if (iType == VIDEODB_CONTENT_MOVIES)
4544 CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4547 else if (iType == VIDEODB_CONTENT_EPISODES)
4549 CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4550 content = "episode";
4552 else if (iType == VIDEODB_CONTENT_TVSHOWS)
4554 CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4557 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4559 CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4560 content = "musicvideo";
4562 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4564 CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4565 CStdString strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4566 m_pDS->exec(strSQL.c_str());
4569 if (!content.empty())
4571 SetSingleValue(iType, idMovie, FieldTitle, strNewMovieTitle);
4572 AnnounceUpdate(content, idMovie);
4577 CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4581 bool CVideoDatabase::UpdateVideoSortTitle(int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType /* = VIDEODB_CONTENT_MOVIES */)
4585 if (NULL == m_pDB.get() || NULL == m_pDS.get())
4587 if (iType != VIDEODB_CONTENT_MOVIES && iType != VIDEODB_CONTENT_TVSHOWS)
4590 CStdString content = "movie";
4591 if (iType == VIDEODB_CONTENT_TVSHOWS)
4594 if (SetSingleValue(iType, idDb, FieldSortTitle, strNewSortTitle))
4596 AnnounceUpdate(content, idDb);
4602 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());
4608 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4609 void CVideoDatabase::EraseVideoSettings()
4613 CLog::Log(LOGINFO, "Deleting settings information for all movies");
4614 m_pDS->exec("delete from settings");
4618 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4622 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4624 return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4627 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4629 return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4632 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4634 return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4637 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4641 if (NULL == m_pDB.get()) return false;
4642 if (NULL == m_pDS.get()) return false;
4645 Filter extFilter = filter;
4646 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4648 if (idContent == VIDEODB_CONTENT_MOVIES)
4650 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4651 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());
4652 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",
4653 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4655 else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4657 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4658 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4659 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",
4660 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4662 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4664 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4665 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());
4666 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",
4667 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4674 if (idContent == VIDEODB_CONTENT_MOVIES)
4676 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4677 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());
4678 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",
4679 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4680 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4682 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4684 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4685 extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4686 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4687 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4689 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4691 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4692 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());
4693 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",
4694 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4695 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4703 extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4704 extFilter.group.clear();
4705 extFilter.order.clear();
4707 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4709 CVideoDbUrl videoUrl;
4710 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4713 int iRowsFound = RunQuery(strSQL);
4714 if (iRowsFound <= 0)
4715 return iRowsFound == 0;
4719 CFileItemPtr pItem(new CFileItem());
4720 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4727 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4729 map<int, pair<CStdString,int> > mapItems;
4730 map<int, pair<CStdString,int> >::iterator it;
4731 while (!m_pDS->eof())
4733 int id = m_pDS->fv(0).get_asInt();
4734 CStdString str = m_pDS->fv(1).get_asString();
4736 // was this already found?
4737 it = mapItems.find(id);
4738 if (it == mapItems.end())
4741 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
4743 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4744 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
4745 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4746 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
4753 for (it = mapItems.begin(); it != mapItems.end(); ++it)
4755 CFileItemPtr pItem(new CFileItem(it->second.first));
4756 pItem->GetVideoInfoTag()->m_iDbId = it->first;
4757 pItem->GetVideoInfoTag()->m_type = type;
4759 CVideoDbUrl itemUrl = videoUrl;
4760 CStdString path = StringUtils::Format("%ld/", it->first);
4761 itemUrl.AppendPath(path);
4762 pItem->SetPath(itemUrl.ToString());
4764 pItem->m_bIsFolder = true;
4765 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4766 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
4767 if (!items.Contains(pItem->GetPath()))
4769 pItem->SetLabelPreformated(true);
4776 while (!m_pDS->eof())
4778 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4779 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
4780 pItem->GetVideoInfoTag()->m_type = type;
4782 CVideoDbUrl itemUrl = videoUrl;
4783 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
4784 itemUrl.AppendPath(path);
4785 pItem->SetPath(itemUrl.ToString());
4787 pItem->m_bIsFolder = true;
4788 pItem->SetLabelPreformated(true);
4789 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4790 { // fv(3) is the number of videos watched, fv(2) is the total number. We set the playcount
4791 // only if the number of videos watched is equal to the total number (i.e. every video watched)
4792 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
4803 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4808 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4810 CStdString mediaType;
4811 if (idContent == VIDEODB_CONTENT_MOVIES)
4812 mediaType = "movie";
4813 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4814 mediaType = "tvshow";
4815 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4816 mediaType = "musicvideo";
4822 if (NULL == m_pDB.get()) return false;
4823 if (NULL == m_pDS.get()) return false;
4825 CStdString strSQL = "SELECT %s FROM taglinks ";
4827 Filter extFilter = filter;
4828 extFilter.fields = "tag.idTag, tag.strTag";
4829 extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
4831 if (idContent == (int)VIDEODB_CONTENT_MOVIES)
4832 extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
4834 extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
4835 extFilter.AppendGroup("taglinks.idTag");
4839 extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
4840 extFilter.group.clear();
4841 extFilter.order.clear();
4843 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4845 // parse the base path to get additional filters
4846 CVideoDbUrl videoUrl;
4847 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4850 int iRowsFound = RunQuery(strSQL);
4851 if (iRowsFound <= 0)
4852 return iRowsFound == 0;
4856 CFileItemPtr pItem(new CFileItem());
4857 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4864 while (!m_pDS->eof())
4866 int idTag = m_pDS->fv(0).get_asInt();
4868 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4869 pItem->m_bIsFolder = true;
4870 pItem->GetVideoInfoTag()->m_iDbId = idTag;
4871 pItem->GetVideoInfoTag()->m_type = "tag";
4873 CVideoDbUrl itemUrl = videoUrl;
4874 CStdString path = StringUtils::Format("%ld/", idTag);
4875 itemUrl.AppendPath(path);
4876 pItem->SetPath(itemUrl.ToString());
4878 if (!items.Contains(pItem->GetPath()))
4880 pItem->SetLabelPreformated(true);
4892 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4897 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool ignoreSingleMovieSets /* = false */)
4899 if (idContent != VIDEODB_CONTENT_MOVIES)
4902 return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
4905 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
4909 if (NULL == m_pDB.get()) return false;
4910 if (NULL == m_pDS.get()) return false;
4912 CVideoDbUrl videoUrl;
4913 if (!videoUrl.FromString(strBaseDir))
4916 Filter setFilter = filter;
4917 setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
4918 if (!setFilter.order.empty())
4919 setFilter.order += ",";
4920 setFilter.order += "sets.idSet";
4922 if (!GetMoviesByWhere(strBaseDir, setFilter, items))
4926 if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
4936 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4941 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4945 if (NULL == m_pDB.get()) return false;
4946 if (NULL == m_pDS.get()) return false;
4948 CVideoDbUrl videoUrl;
4949 if (!videoUrl.FromString(strBaseDir))
4952 CStdString strSQL = "select %s from musicvideoview ";
4953 Filter extFilter = filter;
4954 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
4955 extFilter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
4956 extFilter.AppendJoin(PrepareSQL("join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
4957 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4959 extFilter.fields += " path.strPath";
4960 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath");
4964 videoUrl.AddOption("artistid", idArtist);
4966 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
4970 extFilter.fields = "COUNT(1)";
4971 extFilter.group.clear();
4972 extFilter.order.clear();
4974 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4976 if (!BuildSQL(videoUrl.ToString(), strSQL, extFilter, strSQL, videoUrl))
4979 int iRowsFound = RunQuery(strSQL);
4980 if (iRowsFound <= 0)
4981 return iRowsFound == 0;
4985 CFileItemPtr pItem(new CFileItem());
4986 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4993 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4995 map<int, pair<CStdString,CStdString> > mapAlbums;
4996 map<int, pair<CStdString,CStdString> >::iterator it;
4997 while (!m_pDS->eof())
4999 int lidMVideo = m_pDS->fv(1).get_asInt();
5000 CStdString strAlbum = m_pDS->fv(0).get_asString();
5001 it = mapAlbums.find(lidMVideo);
5002 // was this genre already found?
5003 if (it == mapAlbums.end())
5006 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5007 mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5013 for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5015 if (!it->second.first.empty())
5017 CFileItemPtr pItem(new CFileItem(it->second.first));
5019 CVideoDbUrl itemUrl = videoUrl;
5020 CStdString path = StringUtils::Format("%ld/", it->first);
5021 itemUrl.AppendPath(path);
5022 pItem->SetPath(itemUrl.ToString());
5024 pItem->m_bIsFolder=true;
5025 pItem->SetLabelPreformated(true);
5026 if (!items.Contains(pItem->GetPath()))
5028 pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5036 while (!m_pDS->eof())
5038 if (!m_pDS->fv(0).get_asString().empty())
5040 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5042 CVideoDbUrl itemUrl = videoUrl;
5043 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(1).get_asInt());
5044 itemUrl.AppendPath(path);
5045 pItem->SetPath(itemUrl.ToString());
5047 pItem->m_bIsFolder=true;
5048 pItem->SetLabelPreformated(true);
5049 if (!items.Contains(pItem->GetPath()))
5051 pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5060 // CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5065 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5070 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5072 return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5075 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5077 return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5080 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5082 if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5083 { // set thumbs - ideally this should be in the normal thumb setting routines
5084 for (int i = 0; i < items.Size() && !countOnly; i++)
5086 CFileItemPtr pItem = items[i];
5087 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5088 pItem->SetIconImage("DefaultArtist.png");
5090 pItem->SetIconImage("DefaultActor.png");
5097 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5099 if (NULL == m_pDB.get()) return false;
5100 if (NULL == m_pDS.get()) return false;
5104 // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5105 // point. This means that we *MUST* filter these levels as you'll get double ups. Ideally we'd allow playcount
5106 // to filter through as we normally do for tvshows to save this happening.
5107 // Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5108 // Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5111 // General routine that the other actor/director/writer routines call
5113 // get primary genres for movies
5115 Filter extFilter = filter;
5116 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5118 if (idContent == VIDEODB_CONTENT_MOVIES)
5120 strSQL = "select %s from actors ";
5121 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5122 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",
5123 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5125 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5127 strSQL = "select %s from actors ";
5128 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5129 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",
5130 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5132 else if (idContent == VIDEODB_CONTENT_EPISODES)
5134 strSQL = "select %s from actors ";
5135 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5136 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",
5137 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5139 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5141 strSQL = "select %s from actors ";
5142 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5143 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",
5144 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5151 if (idContent == VIDEODB_CONTENT_MOVIES)
5153 strSQL ="select %s from actors ";
5154 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5155 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",
5156 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5157 extFilter.AppendGroup("actors.idActor");
5159 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5161 strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5162 extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5163 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5164 type.c_str(), type.c_str(), type.c_str()));
5166 else if (idContent == VIDEODB_CONTENT_EPISODES)
5168 strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5169 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5170 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5171 type.c_str(), type.c_str(), type.c_str()));
5172 extFilter.AppendGroup("actors.idActor");
5174 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5176 strSQL = "select %s from actors ";
5177 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5178 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",
5179 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5180 extFilter.AppendGroup("actors.idActor");
5188 extFilter.fields = "COUNT(1)";
5189 extFilter.group.clear();
5190 extFilter.order.clear();
5192 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5194 CVideoDbUrl videoUrl;
5195 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5199 unsigned int time = XbmcThreads::SystemClockMillis();
5200 if (!m_pDS->query(strSQL.c_str())) return false;
5201 CLog::Log(LOGDEBUG, "%s - query took %i ms",
5202 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5203 int iRowsFound = m_pDS->num_rows();
5204 if (iRowsFound == 0)
5212 CFileItemPtr pItem(new CFileItem());
5213 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5220 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5222 map<int, CActor> mapActors;
5223 map<int, CActor>::iterator it;
5225 while (!m_pDS->eof())
5227 int idActor = m_pDS->fv(0).get_asInt();
5229 actor.name = m_pDS->fv(1).get_asString();
5230 actor.thumb = m_pDS->fv(2).get_asString();
5231 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5232 actor.playcount = m_pDS->fv(3).get_asInt();
5233 it = mapActors.find(idActor);
5234 // is this actor already known?
5235 if (it == mapActors.end())
5238 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5239 mapActors.insert(pair<int, CActor>(idActor, actor));
5245 for (it=mapActors.begin();it != mapActors.end();++it)
5247 CFileItemPtr pItem(new CFileItem(it->second.name));
5249 CVideoDbUrl itemUrl = videoUrl;
5250 CStdString path = StringUtils::Format("%ld/", it->first);
5251 itemUrl.AppendPath(path);
5252 pItem->SetPath(itemUrl.ToString());
5254 pItem->m_bIsFolder=true;
5255 pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5256 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5257 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5258 pItem->GetVideoInfoTag()->m_type = type;
5264 while (!m_pDS->eof())
5268 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5270 CVideoDbUrl itemUrl = videoUrl;
5271 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5272 itemUrl.AppendPath(path);
5273 pItem->SetPath(itemUrl.ToString());
5275 pItem->m_bIsFolder=true;
5276 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5277 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5278 pItem->GetVideoInfoTag()->m_type = type;
5279 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5281 // fv(4) is the number of videos watched, fv(3) is the total number. We set the playcount
5282 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5283 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5285 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5286 pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5293 CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5294 return items.Size() > 0;
5299 CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5300 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5307 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5312 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5316 if (NULL == m_pDB.get()) return false;
5317 if (NULL == m_pDS.get()) return false;
5320 Filter extFilter = filter;
5321 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5323 if (idContent == VIDEODB_CONTENT_MOVIES)
5325 strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5326 extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5328 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5330 strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5331 extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5333 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5335 strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5336 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5344 if (idContent == VIDEODB_CONTENT_MOVIES)
5346 strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5347 extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5348 extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5350 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5351 strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5352 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5354 strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5355 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5356 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5362 CVideoDbUrl videoUrl;
5363 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5366 int iRowsFound = RunQuery(strSQL);
5367 if (iRowsFound <= 0)
5368 return iRowsFound == 0;
5370 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5372 map<int, pair<CStdString,int> > mapYears;
5373 map<int, pair<CStdString,int> >::iterator it;
5374 while (!m_pDS->eof())
5377 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5380 time.SetFromDateString(m_pDS->fv(0).get_asString());
5381 lYear = time.GetYear();
5383 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5384 lYear = m_pDS->fv(0).get_asInt();
5385 it = mapYears.find(lYear);
5386 if (it == mapYears.end())
5389 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5391 CStdString year = StringUtils::Format("%d", lYear);
5392 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5393 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5395 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5402 for (it=mapYears.begin();it != mapYears.end();++it)
5406 CFileItemPtr pItem(new CFileItem(it->second.first));
5408 CVideoDbUrl itemUrl = videoUrl;
5409 CStdString path = StringUtils::Format("%ld/", it->first);
5410 itemUrl.AppendPath(path);
5411 pItem->SetPath(itemUrl.ToString());
5413 pItem->m_bIsFolder=true;
5414 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5415 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5421 while (!m_pDS->eof())
5424 CStdString strLabel;
5425 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5428 time.SetFromDateString(m_pDS->fv(0).get_asString());
5429 lYear = time.GetYear();
5430 strLabel = StringUtils::Format("%i",lYear);
5432 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5434 lYear = m_pDS->fv(0).get_asInt();
5435 strLabel = m_pDS->fv(0).get_asString();
5442 CFileItemPtr pItem(new CFileItem(strLabel));
5444 CVideoDbUrl itemUrl = videoUrl;
5445 CStdString path = StringUtils::Format("%ld/", lYear);
5446 itemUrl.AppendPath(path);
5447 pItem->SetPath(itemUrl.ToString());
5449 pItem->m_bIsFolder=true;
5450 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5452 // fv(2) is the number of videos watched, fv(1) is the total number. We set the playcount
5453 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5454 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5457 // take care of dupes ..
5458 if (!items.Contains(pItem->GetPath()))
5470 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5475 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5479 if (NULL == m_pDB.get()) return false;
5480 if (NULL == m_pDS.get()) return false;
5482 // look for duplicate show titles and stack them into a list
5485 CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5486 CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5487 if (!m_pDS->query(strSQL.c_str())) return false;
5488 int iRows = m_pDS->num_rows();
5489 if (iRows == 0) return false; // this should never happen!
5491 { // more than one show, so stack them up
5493 while (!m_pDS->eof())
5495 strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5498 strIn[strIn.size() - 1] = ')'; // replace last , with )
5505 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5510 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5514 if (NULL == m_pDB.get()) return false;
5515 if (NULL == m_pDS.get()) return false;
5517 // parse the base path to get additional filters
5518 CVideoDbUrl videoUrl;
5519 if (!videoUrl.FromString(strBaseDir))
5522 CStdString strIn = PrepareSQL("= %i", idShow);
5523 GetStackedTvShowList(idShow, strIn);
5525 CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5527 "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5528 "seasons.idSeason, "
5529 "count(1), count(files.playCount) "
5530 "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);
5533 filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5534 "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5535 "JOIN files ON files.idFile = episodeview.idFile "
5536 "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5537 "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5538 filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5539 filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5541 videoUrl.AddOption("tvshowid", idShow);
5544 videoUrl.AddOption("actorid", idActor);
5545 else if (idDirector != -1)
5546 videoUrl.AddOption("directorid", idDirector);
5547 else if (idGenre != -1)
5548 videoUrl.AddOption("genreid", idGenre);
5549 else if (idYear != -1)
5550 videoUrl.AddOption("year", idYear);
5552 if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5555 int iRowsFound = RunQuery(strSQL);
5556 if (iRowsFound <= 0)
5557 return iRowsFound == 0;
5559 // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5560 CStdString showTitle = m_pDS->fv(2).get_asString();
5561 CStdString showPlot = m_pDS->fv(3).get_asString();
5562 CStdString showPremiered = m_pDS->fv(4).get_asString();
5563 CStdString showStudio = m_pDS->fv(6).get_asString();
5564 CStdString showMPAARating = m_pDS->fv(7).get_asString();
5566 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5568 map<int, CSeason> mapSeasons;
5569 map<int, CSeason>::iterator it;
5570 while (!m_pDS->eof())
5572 int iSeason = m_pDS->fv(0).get_asInt();
5573 it = mapSeasons.find(iSeason);
5575 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5580 if (it == mapSeasons.end())
5583 season.path = m_pDS->fv(1).get_asString();
5584 season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5585 season.id = m_pDS->fv(8).get_asInt();
5586 season.numEpisodes = m_pDS->fv(9).get_asInt();
5587 season.numWatched = m_pDS->fv(10).get_asInt();
5588 mapSeasons.insert(make_pair(iSeason, season));
5594 for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5596 int iSeason = it->first;
5597 CStdString strLabel;
5599 strLabel = g_localizeStrings.Get(20381);
5601 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5602 CFileItemPtr pItem(new CFileItem(strLabel));
5604 CVideoDbUrl itemUrl = videoUrl;
5605 CStdString strDir = StringUtils::Format("%ld/", it->first);
5606 itemUrl.AppendPath(strDir);
5607 pItem->SetPath(itemUrl.ToString());
5609 pItem->m_bIsFolder=true;
5610 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5611 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5612 pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5613 pItem->GetVideoInfoTag()->m_type = "season";
5614 pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5615 pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5616 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5617 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5618 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5619 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5620 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5621 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5622 pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5623 pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5624 pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5625 pItem->SetProperty("watchedepisodes", it->second.numWatched);
5626 pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5627 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5628 pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5629 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5635 while (!m_pDS->eof())
5637 int iSeason = m_pDS->fv(0).get_asInt();
5638 CStdString strLabel;
5640 strLabel = g_localizeStrings.Get(20381);
5642 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5643 CFileItemPtr pItem(new CFileItem(strLabel));
5645 CVideoDbUrl itemUrl = videoUrl;
5646 CStdString strDir = StringUtils::Format("%ld/", iSeason);
5647 itemUrl.AppendPath(strDir);
5648 pItem->SetPath(itemUrl.ToString());
5650 pItem->m_bIsFolder=true;
5651 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5652 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5653 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5654 pItem->GetVideoInfoTag()->m_type = "season";
5655 pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5656 pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5657 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5658 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5659 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5660 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5661 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5662 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5663 int totalEpisodes = m_pDS->fv(9).get_asInt();
5664 int watchedEpisodes = m_pDS->fv(10).get_asInt();
5665 pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5666 pItem->SetProperty("totalepisodes", totalEpisodes);
5667 pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5668 pItem->SetProperty("watchedepisodes", watchedEpisodes);
5669 pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5670 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5671 pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5672 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5679 // now add any linked movies
5680 if (getLinkedMovies)
5683 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5684 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5685 CFileItemList movieItems;
5686 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
5688 if (movieItems.Size() > 0)
5689 items.Append(movieItems);
5696 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5701 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5703 if (NULL == m_pDB.get() || NULL == m_pDS.get())
5706 if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5709 SortDescription sorting = sortDescription;
5710 if (sortDescription.sortBy == SortByFile ||
5711 sortDescription.sortBy == SortByTitle ||
5712 sortDescription.sortBy == SortBySortTitle ||
5713 sortDescription.sortBy == SortByLabel ||
5714 sortDescription.sortBy == SortByDateAdded ||
5715 sortDescription.sortBy == SortByRating ||
5716 sortDescription.sortBy == SortByYear ||
5717 sortDescription.sortBy == SortByLastPlayed ||
5718 sortDescription.sortBy == SortByPlaycount)
5719 sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5721 bool success = false;
5724 case MediaTypeMovie:
5725 success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5728 case MediaTypeTvShow:
5729 success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
5732 case MediaTypeEpisode:
5733 success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
5736 case MediaTypeMusicVideo:
5737 success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
5744 items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
5748 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5750 CVideoDbUrl videoUrl;
5751 if (!videoUrl.FromString(strBaseDir))
5754 return GetItems(strBaseDir, videoUrl.GetType(), videoUrl.GetItemType(), items, filter, sortDescription);
5757 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, const CStdString &mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5759 VIDEODB_CONTENT_TYPE contentType;
5760 if (mediaType.Equals("movies"))
5761 contentType = VIDEODB_CONTENT_MOVIES;
5762 else if (mediaType.Equals("tvshows"))
5764 if (itemType.Equals("episodes"))
5765 contentType = VIDEODB_CONTENT_EPISODES;
5767 contentType = VIDEODB_CONTENT_TVSHOWS;
5769 else if (mediaType.Equals("musicvideos"))
5770 contentType = VIDEODB_CONTENT_MUSICVIDEOS;
5774 return GetItems(strBaseDir, contentType, itemType, items, filter, sortDescription);
5777 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, VIDEODB_CONTENT_TYPE mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5779 if (itemType.Equals("movies") && (mediaType == VIDEODB_CONTENT_MOVIES || mediaType == VIDEODB_CONTENT_MOVIE_SETS))
5780 return GetMoviesByWhere(strBaseDir, filter, items, sortDescription);
5781 else if (itemType.Equals("tvshows") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5782 return GetTvShowsByWhere(strBaseDir, filter, items, sortDescription);
5783 else if (itemType.Equals("musicvideos") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5784 return GetMusicVideosByWhere(strBaseDir, filter, items, true, sortDescription);
5785 else if (itemType.Equals("episodes") && mediaType == VIDEODB_CONTENT_EPISODES)
5786 return GetEpisodesByWhere(strBaseDir, filter, items, true, sortDescription);
5787 else if (itemType.Equals("seasons") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5788 return GetSeasonsNav(strBaseDir, items);
5789 else if (itemType.Equals("genres"))
5790 return GetGenresNav(strBaseDir, items, mediaType, filter);
5791 else if (itemType.Equals("years"))
5792 return GetYearsNav(strBaseDir, items, mediaType, filter);
5793 else if (itemType.Equals("actors"))
5794 return GetActorsNav(strBaseDir, items, mediaType, filter);
5795 else if (itemType.Equals("directors"))
5796 return GetDirectorsNav(strBaseDir, items, mediaType, filter);
5797 else if (itemType.Equals("writers"))
5798 return GetWritersNav(strBaseDir, items, mediaType, filter);
5799 else if (itemType.Equals("studios"))
5800 return GetStudiosNav(strBaseDir, items, mediaType, filter);
5801 else if (itemType.Equals("sets"))
5802 return GetSetsNav(strBaseDir, items, mediaType, filter);
5803 else if (itemType.Equals("countries"))
5804 return GetCountriesNav(strBaseDir, items, mediaType, filter);
5805 else if (itemType.Equals("tags"))
5806 return GetTagsNav(strBaseDir, items, mediaType, filter);
5807 else if (itemType.Equals("artists") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5808 return GetActorsNav(strBaseDir, items, mediaType, filter);
5809 else if (itemType.Equals("albums") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5810 return GetMusicVideoAlbumsNav(strBaseDir, items, -1, filter);
5815 CStdString CVideoDatabase::GetItemById(const CStdString &itemType, int id)
5817 if (itemType.Equals("genres"))
5818 return GetGenreById(id);
5819 else if (itemType.Equals("years"))
5820 return StringUtils::Format("%d", id);
5821 else if (itemType.Equals("actors") || itemType.Equals("directors") || itemType.Equals("artists"))
5822 return GetPersonById(id);
5823 else if (itemType.Equals("studios"))
5824 return GetStudioById(id);
5825 else if (itemType.Equals("sets"))
5826 return GetSetById(id);
5827 else if (itemType.Equals("countries"))
5828 return GetCountryById(id);
5829 else if (itemType.Equals("tags"))
5830 return GetTagById(id);
5831 else if (itemType.Equals("albums"))
5832 return GetMusicVideoAlbumById(id);
5837 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
5838 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
5839 int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
5840 const SortDescription &sortDescription /* = SortDescription() */)
5842 CVideoDbUrl videoUrl;
5843 if (!videoUrl.FromString(strBaseDir))
5847 videoUrl.AddOption("genreid", idGenre);
5848 else if (idCountry > 0)
5849 videoUrl.AddOption("countryid", idCountry);
5850 else if (idStudio > 0)
5851 videoUrl.AddOption("studioid", idStudio);
5852 else if (idDirector > 0)
5853 videoUrl.AddOption("directorid", idDirector);
5854 else if (idYear > 0)
5855 videoUrl.AddOption("year", idYear);
5856 else if (idActor > 0)
5857 videoUrl.AddOption("actorid", idActor);
5859 videoUrl.AddOption("setid", idSet);
5861 videoUrl.AddOption("tagid", idTag);
5864 return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
5867 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5874 if (NULL == m_pDB.get()) return false;
5875 if (NULL == m_pDS.get()) return false;
5877 // parse the base path to get additional filters
5878 CVideoDbUrl videoUrl;
5879 Filter extFilter = filter;
5880 SortDescription sorting = sortDescription;
5881 if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
5886 CStdString strSQL = "select %s from movieview ";
5887 CStdString strSQLExtra;
5888 if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
5891 // Apply the limiting directly here if there's no special sorting but limiting
5892 if (extFilter.limit.empty() &&
5893 sorting.sortBy == SortByNone &&
5894 (sorting.limitStart > 0 || sorting.limitEnd > 0))
5896 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
5897 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
5900 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
5902 int iRowsFound = RunQuery(strSQL);
5903 if (iRowsFound <= 0)
5904 return iRowsFound == 0;
5906 // store the total value of items as a property
5907 if (total < iRowsFound)
5909 items.SetProperty("total", total);
5911 DatabaseResults results;
5912 results.reserve(iRowsFound);
5914 if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
5917 // get data from returned rows
5918 items.Reserve(results.size());
5919 const query_data &data = m_pDS->get_result_set().records;
5920 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
5922 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
5923 const dbiplus::sql_record* const record = data.at(targetRow);
5925 CVideoInfoTag movie = GetDetailsForMovie(record);
5926 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
5927 g_passwordManager.bMasterUser ||
5928 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
5930 CFileItemPtr pItem(new CFileItem(movie));
5932 CVideoDbUrl itemUrl = videoUrl;
5933 CStdString path = StringUtils::Format("%ld", movie.m_iDbId);
5934 itemUrl.AppendPath(path);
5935 pItem->SetPath(itemUrl.ToString());
5937 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
5948 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5953 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
5954 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
5955 const SortDescription &sortDescription /* = SortDescription() */)
5957 CVideoDbUrl videoUrl;
5958 if (!videoUrl.FromString(strBaseDir))
5962 videoUrl.AddOption("genreid", idGenre);
5963 else if (idStudio != -1)
5964 videoUrl.AddOption("studioid", idStudio);
5965 else if (idDirector != -1)
5966 videoUrl.AddOption("directorid", idDirector);
5967 else if (idYear != -1)
5968 videoUrl.AddOption("year", idYear);
5969 else if (idActor != -1)
5970 videoUrl.AddOption("actorid", idActor);
5971 else if (idTag != -1)
5972 videoUrl.AddOption("tagid", idTag);
5975 return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
5978 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5984 if (NULL == m_pDB.get()) return false;
5985 if (NULL == m_pDS.get()) return false;
5989 CStdString strSQL = "SELECT %s FROM tvshowview ";
5990 CVideoDbUrl videoUrl;
5991 CStdString strSQLExtra;
5992 Filter extFilter = filter;
5993 SortDescription sorting = sortDescription;
5994 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
5997 // Apply the limiting directly here if there's no special sorting but limiting
5998 if (extFilter.limit.empty() &&
5999 sorting.sortBy == SortByNone &&
6000 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6002 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6003 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6006 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6008 int iRowsFound = RunQuery(strSQL);
6009 if (iRowsFound <= 0)
6010 return iRowsFound == 0;
6012 // store the total value of items as a property
6013 if (total < iRowsFound)
6015 items.SetProperty("total", total);
6017 DatabaseResults results;
6018 results.reserve(iRowsFound);
6019 if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
6022 // get data from returned rows
6023 items.Reserve(results.size());
6024 const query_data &data = m_pDS->get_result_set().records;
6025 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6027 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6028 const dbiplus::sql_record* const record = data.at(targetRow);
6030 CFileItemPtr pItem(new CFileItem());
6031 CVideoInfoTag movie = GetDetailsForTvShow(record, false, pItem.get());
6032 if ((CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6033 g_passwordManager.bMasterUser ||
6034 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video"))) &&
6035 (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
6037 pItem->SetFromVideoInfoTag(movie);
6039 CVideoDbUrl itemUrl = videoUrl;
6040 CStdString path = StringUtils::Format("%ld/", record->at(0).get_asInt());
6041 itemUrl.AppendPath(path);
6042 pItem->SetPath(itemUrl.ToString());
6044 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6049 Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6057 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6062 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6064 if (maintainSortOrder)
6066 // save current sort order
6067 for (int i = 0; i < items.Size(); i++)
6068 items[i]->m_iprogramCount = i;
6073 case VIDEODB_CONTENT_TVSHOWS:
6076 items.Sort(SortBySortTitle, SortOrderAscending);
6079 while (i < items.Size())
6081 CFileItemPtr pItem = items.Get(i);
6082 CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6083 CStdString strFanArt = pItem->GetArt("fanart");
6086 bool bStacked = false;
6087 while (j < items.Size())
6089 CFileItemPtr jItem = items.Get(j);
6091 // matching title? append information
6092 if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6094 if (jItem->GetVideoInfoTag()->m_premiered !=
6095 pItem->GetVideoInfoTag()->m_premiered)
6102 // increment episode counts
6103 pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6104 pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6105 pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6106 pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6107 pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6109 // adjust lastplayed
6110 if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6111 pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6113 // check for fanart if not already set
6114 if (strFanArt.empty())
6115 strFanArt = jItem->GetArt("fanart");
6117 // remove duplicate entry
6120 // no match? exit loop
6124 // update playcount and fanart
6127 pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6128 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6129 if (!strFanArt.empty())
6130 pItem->SetArt("fanart", strFanArt);
6132 // increment i to j which is the next item
6137 // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6138 // so that if we eventually want to stack during scan we can utilize it.
6140 case VIDEODB_CONTENT_EPISODES:
6142 // sort by ShowTitle, Episode, Filename
6143 items.Sort(SortByEpisodeNumber, SortOrderAscending);
6146 while (i < items.Size())
6148 CFileItemPtr pItem = items.Get(i);
6149 CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6150 int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6151 int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6152 //CStdString strFanArt = pItem->GetArt("fanart");
6154 // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6155 CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6156 bool bDvdFolder = StringUtils::EndsWithNoCase(strFileNameAndPath, "video_ts.ifo");
6158 vector<CStdString> paths;
6159 paths.push_back(strFileNameAndPath);
6160 CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6163 int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6164 while (j < items.Size())
6166 CFileItemPtr jItem = items.Get(j);
6167 const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6168 CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6170 CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6171 // compare path, season, episode
6174 jTag->m_strPath.Equals(strPath) &&
6175 jTag->m_iSeason == iSeason &&
6176 jTag->m_iEpisode == iEpisode
6179 // keep checking to see if this is dvd folder
6182 bDvdFolder = StringUtils::EndsWithNoCase(jFileNameAndPath, "video_ts.ifo");
6183 // if we have a dvd folder, we stack differently
6186 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6188 paths.push_back(jFileNameAndPath);
6192 // increment playcount
6193 iPlayCount += jTag->m_playCount;
6195 // episodes dont have fanart yet
6196 //if (strFanArt.empty())
6197 // strFanArt = jItem->GetArt("fanart");
6199 paths.push_back(jFileNameAndPath);
6203 // remove duplicate entry
6207 // no match? exit loop
6211 // update playcount and fanart if we have a stacked entry
6212 if (paths.size() > 1)
6214 CStackDirectory dir;
6215 CStdString strStack;
6216 dir.ConstructStackPath(paths, strStack);
6217 pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6218 pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6219 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6221 // episodes dont have fanart yet
6222 //if (!strFanArt.empty())
6223 // pItem->SetArt("fanart", strFanArt);
6225 // increment i to j which is the next item
6231 // stack other types later
6235 if (maintainSortOrder)
6237 // restore original sort order - essential for smartplaylists
6238 items.Sort(SortByProgramCount, SortOrderAscending);
6242 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6244 CVideoDbUrl videoUrl;
6245 if (!videoUrl.FromString(strBaseDir))
6251 strIn = PrepareSQL("= %i", idShow);
6252 GetStackedTvShowList(idShow, strIn);
6254 videoUrl.AddOption("tvshowid", idShow);
6256 videoUrl.AddOption("season", idSeason);
6259 videoUrl.AddOption("genreid", idGenre);
6260 else if (idYear !=-1)
6261 videoUrl.AddOption("year", idYear);
6262 else if (idActor != -1)
6263 videoUrl.AddOption("actorid", idActor);
6265 else if (idYear != -1)
6266 videoUrl.AddOption("year", idYear);
6268 if (idDirector != -1)
6269 videoUrl.AddOption("directorid", idDirector);
6272 bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6274 if (idSeason == -1 && idShow != -1)
6275 { // add any linked movies
6277 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6278 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6279 CFileItemList movieItems;
6280 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
6282 if (movieItems.Size() > 0)
6283 items.Append(movieItems);
6289 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6296 if (NULL == m_pDB.get()) return false;
6297 if (NULL == m_pDS.get()) return false;
6301 CStdString strSQL = "select %s from episodeview ";
6302 CVideoDbUrl videoUrl;
6303 CStdString strSQLExtra;
6304 Filter extFilter = filter;
6305 SortDescription sorting = sortDescription;
6306 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6309 // Apply the limiting directly here if there's no special sorting but limiting
6310 if (extFilter.limit.empty() &&
6311 sorting.sortBy == SortByNone &&
6312 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6314 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6315 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6318 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6320 int iRowsFound = RunQuery(strSQL);
6321 if (iRowsFound <= 0)
6322 return iRowsFound == 0;
6324 // store the total value of items as a property
6325 if (total < iRowsFound)
6327 items.SetProperty("total", total);
6329 DatabaseResults results;
6330 results.reserve(iRowsFound);
6331 if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6334 // get data from returned rows
6335 items.Reserve(results.size());
6336 CLabelFormatter formatter("%H. %T", "");
6338 const query_data &data = m_pDS->get_result_set().records;
6339 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6341 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6342 const dbiplus::sql_record* const record = data.at(targetRow);
6344 CVideoInfoTag movie = GetDetailsForEpisode(record);
6345 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6346 g_passwordManager.bMasterUser ||
6347 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6349 CFileItemPtr pItem(new CFileItem(movie));
6350 formatter.FormatLabel(pItem.get());
6352 int idEpisode = record->at(0).get_asInt();
6354 CVideoDbUrl itemUrl = videoUrl;
6356 if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6357 path = StringUtils::Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6359 path = StringUtils::Format("%ld", idEpisode);
6360 itemUrl.AppendPath(path);
6361 pItem->SetPath(itemUrl.ToString());
6363 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6364 pItem->m_dateTime = movie.m_firstAired;
6365 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6376 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6381 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() */)
6383 CVideoDbUrl videoUrl;
6384 if (!videoUrl.FromString(strBaseDir))
6388 videoUrl.AddOption("genreid", idGenre);
6389 else if (idStudio != -1)
6390 videoUrl.AddOption("studioid", idStudio);
6391 else if (idDirector != -1)
6392 videoUrl.AddOption("directorid", idDirector);
6393 else if (idYear !=-1)
6394 videoUrl.AddOption("year", idYear);
6395 else if (idArtist != -1)
6396 videoUrl.AddOption("artistid", idArtist);
6397 else if (idTag != -1)
6398 videoUrl.AddOption("tagid", idTag);
6400 videoUrl.AddOption("albumid", idAlbum);
6403 return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6406 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6409 filter.order = "dateAdded desc, idMovie desc";
6410 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6411 return GetMoviesByWhere(strBaseDir, filter, items);
6414 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6417 filter.order = "dateAdded desc, idEpisode desc";
6418 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6419 return GetEpisodesByWhere(strBaseDir, filter, items, false);
6422 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6425 filter.order = "dateAdded desc, idMVideo desc";
6426 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6427 return GetMusicVideosByWhere(strBaseDir, filter, items);
6430 CStdString CVideoDatabase::GetGenreById(int id)
6432 return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6435 CStdString CVideoDatabase::GetCountryById(int id)
6437 return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6440 CStdString CVideoDatabase::GetSetById(int id)
6442 return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6445 CStdString CVideoDatabase::GetTagById(int id)
6447 return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6450 CStdString CVideoDatabase::GetPersonById(int id)
6452 return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6455 CStdString CVideoDatabase::GetStudioById(int id)
6457 return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6460 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6462 return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6465 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6467 return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6470 bool CVideoDatabase::HasSets() const
6474 if (NULL == m_pDB.get()) return false;
6475 if (NULL == m_pDS.get()) return false;
6477 m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6478 "JOIN sets ON sets.idSet = movieview.idSet "
6479 "GROUP BY movieview.idSet HAVING c>1");
6481 bool bResult = (m_pDS->num_rows() > 0);
6487 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6492 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6496 if (NULL == m_pDB.get()) return false;
6497 if (NULL == m_pDS2.get()) return false;
6499 // make sure we use m_pDS2, as this is called in loops using m_pDS
6500 CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6501 m_pDS2->query( strSQL.c_str() );
6505 result=m_pDS2->fv(0).get_asInt();
6512 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6517 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6520 sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6521 CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6524 return atoi(id.c_str());
6527 bool CVideoDatabase::HasContent()
6529 return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6530 HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6531 HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6534 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6536 bool result = false;
6539 if (NULL == m_pDB.get()) return false;
6540 if (NULL == m_pDS.get()) return false;
6543 if (type == VIDEODB_CONTENT_MOVIES)
6544 sql = "select count(1) from movie";
6545 else if (type == VIDEODB_CONTENT_TVSHOWS)
6546 sql = "select count(1) from tvshow";
6547 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6548 sql = "select count(1) from musicvideo";
6549 m_pDS->query( sql.c_str() );
6552 result = (m_pDS->fv(0).get_asInt() > 0);
6558 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6563 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6567 if (NULL == m_pDB.get()) return 0;
6568 if (NULL == m_pDS.get()) return 0;
6570 CStdString strSQL = StringUtils::Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6571 m_pDS->query( strSQL.c_str() );
6575 iResult = m_pDS->fv("nummovies").get_asInt();
6582 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6587 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6589 SScanSettings settings;
6590 return GetScraperForPath(strPath, settings);
6593 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6596 return GetScraperForPath(strPath, settings, dummy);
6599 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6601 foundDirectly = false;
6604 if (strPath.empty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6607 CStdString strPath2;
6609 if (URIUtils::IsMultiPath(strPath))
6610 strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6614 CStdString strPath1 = URIUtils::GetDirectory(strPath2);
6615 int idPath = GetPathId(strPath1);
6619 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);
6620 m_pDS->query( strSQL.c_str() );
6624 CONTENT_TYPE content = CONTENT_NONE;
6626 { // path is stored in db
6628 if (m_pDS->fv("path.exclude").get_asBool())
6630 settings.exclude = true;
6632 return ScraperPtr();
6634 settings.exclude = false;
6636 // try and ascertain scraper for this path
6637 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6638 StringUtils::ToLower(strcontent);
6639 content = TranslateContent(strcontent);
6641 //FIXME paths stored should not have empty strContent
6642 //assert(content != CONTENT_NONE);
6643 CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6646 if (!scraperID.empty() &&
6647 CAddonMgr::Get().GetAddon(scraperID, addon))
6649 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6651 return ScraperPtr();
6653 // store this path's content & settings
6654 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6655 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6656 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6657 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6661 if (content == CONTENT_NONE)
6662 { // this path is not saved in db
6663 // we must drill up until a scraper is configured
6664 CStdString strParent;
6665 while (URIUtils::GetParentPath(strPath1, strParent))
6669 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());
6670 m_pDS->query(strSQL.c_str());
6672 CONTENT_TYPE content = CONTENT_NONE;
6676 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6677 StringUtils::ToLower(strcontent);
6678 if (m_pDS->fv("path.exclude").get_asBool())
6680 settings.exclude = true;
6686 content = TranslateContent(strcontent);
6689 if (content != CONTENT_NONE &&
6690 CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6692 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6693 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6694 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6695 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6696 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6697 settings.exclude = false;
6701 strPath1 = strParent;
6706 if (!scraper || scraper->Content() == CONTENT_NONE)
6707 return ScraperPtr();
6709 if (scraper->Content() == CONTENT_TVSHOWS)
6711 settings.recurse = 0;
6712 if(settings.parent_name) // single show
6714 settings.parent_name_root = settings.parent_name = (iFound == 1);
6718 settings.parent_name_root = settings.parent_name = (iFound == 2);
6721 else if (scraper->Content() == CONTENT_MOVIES)
6723 settings.recurse = settings.recurse - (iFound-1);
6724 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6726 else if (scraper->Content() == CONTENT_MUSICVIDEOS)
6728 settings.recurse = settings.recurse - (iFound-1);
6729 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6734 return ScraperPtr();
6736 foundDirectly = (iFound == 1);
6741 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6743 return ScraperPtr();
6746 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
6748 SScanSettings settings;
6749 bool foundDirectly = false;
6750 ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
6753 if (scraper->Content() == CONTENT_TVSHOWS)
6754 { // check for episodes or seasons. Assumptions are:
6755 // 1. if episodes are in the path then we're in episodes.
6756 // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
6757 // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
6758 CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
6759 m_pDS->query( sql.c_str() );
6760 if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
6762 return foundDirectly ? "tvshows" : "seasons";
6764 return TranslateContent(scraper->Content());
6769 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
6775 if (NULL == m_pDB.get()) return;
6776 if (NULL == m_pDS.get()) return;
6778 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6779 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());
6781 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6782 m_pDS->query( strSQL.c_str() );
6784 while (!m_pDS->eof())
6786 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6787 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6788 *CMediaSourceSettings::Get().GetSources("video")))
6794 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6795 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6796 pItem->SetPath("videodb://movies/genres/"+ strDir);
6797 pItem->m_bIsFolder=true;
6805 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6809 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
6815 if (NULL == m_pDB.get()) return;
6816 if (NULL == m_pDS.get()) return;
6818 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6819 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());
6821 strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
6822 m_pDS->query( strSQL.c_str() );
6824 while (!m_pDS->eof())
6826 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6827 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6828 *CMediaSourceSettings::Get().GetSources("video")))
6834 CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
6835 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
6836 pItem->SetPath("videodb://movies/genres/"+ strDir);
6837 pItem->m_bIsFolder=true;
6845 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6849 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
6855 if (NULL == m_pDB.get()) return;
6856 if (NULL == m_pDS.get()) return;
6858 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6859 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());
6861 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6862 m_pDS->query( strSQL.c_str() );
6864 while (!m_pDS->eof())
6866 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6867 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6873 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6874 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6875 pItem->SetPath("videodb://tvshows/genres/"+ strDir);
6876 pItem->m_bIsFolder=true;
6884 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6888 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
6894 if (NULL == m_pDB.get()) return;
6895 if (NULL == m_pDS.get()) return;
6897 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6898 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());
6900 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());
6901 m_pDS->query( strSQL.c_str() );
6903 while (!m_pDS->eof())
6905 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6906 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6912 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6913 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6914 pItem->SetPath("videodb://movies/actors/"+ strDir);
6915 pItem->m_bIsFolder=true;
6923 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6927 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
6933 if (NULL == m_pDB.get()) return;
6934 if (NULL == m_pDS.get()) return;
6936 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6937 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());
6939 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());
6940 m_pDS->query( strSQL.c_str() );
6942 while (!m_pDS->eof())
6944 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6945 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6951 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6952 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6953 pItem->SetPath("videodb://tvshows/actors/"+ strDir);
6954 pItem->m_bIsFolder=true;
6962 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6966 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
6972 if (NULL == m_pDB.get()) return;
6973 if (NULL == m_pDS.get()) return;
6976 if (!strSearch.empty())
6977 strLike = "and actors.strActor like '%%%s%%'";
6978 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6979 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());
6981 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
6982 m_pDS->query( strSQL.c_str() );
6984 while (!m_pDS->eof())
6986 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6987 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6993 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6994 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6995 pItem->SetPath("videodb://musicvideos/artists/"+ strDir);
6996 pItem->m_bIsFolder=true;
7004 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7008 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
7014 if (NULL == m_pDB.get()) return;
7015 if (NULL == m_pDS.get()) return;
7017 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7018 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());
7020 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
7021 m_pDS->query( strSQL.c_str() );
7023 while (!m_pDS->eof())
7025 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7026 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7032 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7033 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7034 pItem->SetPath("videodb://musicvideos/genres/"+ strDir);
7035 pItem->m_bIsFolder=true;
7043 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7047 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7053 if (NULL == m_pDB.get()) return;
7054 if (NULL == m_pDS.get()) return;
7056 strSQL = StringUtils::Format("SELECT DISTINCT"
7057 " musicvideo.c%02d,"
7058 " musicvideo.idMVideo,"
7063 " files.idFile=musicvideo.idFile"
7065 " path.idPath=files.idPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
7066 if (!strSearch.empty())
7067 strSQL += PrepareSQL(" WHERE musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM, strSearch.c_str());
7069 m_pDS->query( strSQL.c_str() );
7071 while (!m_pDS->eof())
7073 if (m_pDS->fv(0).get_asString().empty())
7079 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7080 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7086 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7087 CStdString strDir = StringUtils::Format("%ld", m_pDS->fv(1).get_asInt());
7088 pItem->SetPath("videodb://musicvideos/titles/"+ strDir);
7089 pItem->m_bIsFolder=false;
7097 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7101 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7107 if (NULL == m_pDB.get()) return;
7108 if (NULL == m_pDS.get()) return;
7110 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7111 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());
7113 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());
7114 m_pDS->query( strSQL.c_str() );
7116 while (!m_pDS->eof())
7118 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7119 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7125 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7126 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7128 pItem->SetPath("videodb://"+ strDir);
7129 pItem->m_bIsFolder=false;
7137 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7141 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7148 if (NULL == m_pDB.get()) return false;
7149 if (NULL == m_pDS.get()) return false;
7153 CStdString strSQL = "select %s from musicvideoview ";
7154 CVideoDbUrl videoUrl;
7155 CStdString strSQLExtra;
7156 Filter extFilter = filter;
7157 SortDescription sorting = sortDescription;
7158 if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7161 // Apply the limiting directly here if there's no special sorting but limiting
7162 if (extFilter.limit.empty() &&
7163 sorting.sortBy == SortByNone &&
7164 (sorting.limitStart > 0 || sorting.limitEnd > 0))
7166 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7167 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7170 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7172 int iRowsFound = RunQuery(strSQL);
7173 if (iRowsFound <= 0)
7174 return iRowsFound == 0;
7176 // store the total value of items as a property
7177 if (total < iRowsFound)
7179 items.SetProperty("total", total);
7181 DatabaseResults results;
7182 results.reserve(iRowsFound);
7183 if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7186 // get data from returned rows
7187 items.Reserve(results.size());
7188 // get songs from returned subtable
7189 const query_data &data = m_pDS->get_result_set().records;
7190 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7192 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7193 const dbiplus::sql_record* const record = data.at(targetRow);
7195 CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7196 if (!checkLocks || CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7197 g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
7199 CFileItemPtr item(new CFileItem(musicvideo));
7201 CVideoDbUrl itemUrl = videoUrl;
7202 CStdString path = StringUtils::Format("%ld", record->at(0).get_asInt());
7203 itemUrl.AppendPath(path);
7204 item->SetPath(itemUrl.ToString());
7206 item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7217 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7222 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7226 if (NULL == m_pDB.get()) return 0;
7227 if (NULL == m_pDS.get()) return 0;
7229 CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7230 if (!m_pDS->query(strSQL.c_str())) return 0;
7232 if (m_pDS->num_rows() == 0)
7237 songIDs.reserve(m_pDS->num_rows());
7238 while (!m_pDS->eof())
7240 songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7244 return songIDs.size();
7248 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7253 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7259 if (NULL == m_pDB.get()) return false;
7260 if (NULL == m_pDS.get()) return false;
7262 // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7263 CStdString strSQL = StringUtils::Format("select * from musicvideoview where %s", strWhere.c_str());
7264 strSQL += PrepareSQL(" order by RANDOM() limit 1");
7265 CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7267 if (!m_pDS->query(strSQL.c_str()))
7269 int iRowsFound = m_pDS->num_rows();
7270 if (iRowsFound != 1)
7275 *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7276 CStdString path = StringUtils::Format("videodb://musicvideos/titles/%ld",item->GetVideoInfoTag()->m_iDbId);
7277 item->SetPath(path);
7278 idSong = m_pDS->fv("idMVideo").get_asInt();
7279 item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7285 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7290 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7294 if (NULL == m_pDB.get()) return -1;
7295 if (NULL == m_pDS.get()) return -1;
7298 if (strAlbum.empty() && strTitle.empty())
7299 { // we want to return matching artists only
7300 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7301 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());
7303 strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7306 { // we want to return the matching musicvideo
7307 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7308 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());
7310 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());
7312 m_pDS->query( strSQL.c_str() );
7317 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7318 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7324 int lResult = m_pDS->fv(0).get_asInt();
7330 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7335 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7341 if (NULL == m_pDB.get()) return;
7342 if (NULL == m_pDS.get()) return;
7344 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7345 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());
7347 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());
7348 m_pDS->query( strSQL.c_str() );
7350 while (!m_pDS->eof())
7352 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7353 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7359 int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7360 int setId = m_pDS->fv("movie.idSet").get_asInt();
7361 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7363 if (setId <= 0 || !CSettings::Get().GetBool("videolibrary.groupmoviesets"))
7364 path = StringUtils::Format("videodb://movies/titles/%i", movieId);
7366 path = StringUtils::Format("videodb://movies/sets/%i/%i", setId, movieId);
7367 pItem->SetPath(path);
7368 pItem->m_bIsFolder=false;
7376 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7380 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7386 if (NULL == m_pDB.get()) return;
7387 if (NULL == m_pDS.get()) return;
7389 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7390 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());
7392 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());
7393 m_pDS->query( strSQL.c_str() );
7395 while (!m_pDS->eof())
7397 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7398 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7404 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7405 CStdString strDir = StringUtils::Format("tvshows/titles/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7407 pItem->SetPath("videodb://"+ strDir);
7408 pItem->m_bIsFolder=true;
7409 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7417 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7421 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7427 if (NULL == m_pDB.get()) return;
7428 if (NULL == m_pDS.get()) return;
7430 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7431 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());
7433 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());
7434 m_pDS->query( strSQL.c_str() );
7436 while (!m_pDS->eof())
7438 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7439 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7445 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7446 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());
7447 pItem->SetPath(path);
7448 pItem->m_bIsFolder=false;
7456 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7460 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7462 // Alternative searching - not quite as fast though due to
7463 // retrieving all information
7464 // 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()));
7465 // GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
7470 if (NULL == m_pDB.get()) return;
7471 if (NULL == m_pDS.get()) return;
7473 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7474 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());
7476 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());
7477 m_pDS->query( strSQL.c_str() );
7479 while (!m_pDS->eof())
7481 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7482 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7488 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7489 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7491 pItem->SetPath("videodb://"+ strDir);
7492 pItem->m_bIsFolder=false;
7500 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7504 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7506 // Alternative searching - not quite as fast though due to
7507 // retrieving all information
7509 // 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());
7510 // 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());
7511 // GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
7517 if (NULL == m_pDB.get()) return;
7518 if (NULL == m_pDS.get()) return;
7520 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7521 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());
7523 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());
7524 m_pDS->query( strSQL.c_str() );
7526 while (!m_pDS->eof())
7528 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7529 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7535 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7536 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());
7537 pItem->SetPath(path);
7538 pItem->m_bIsFolder=false;
7546 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7550 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7556 if (NULL == m_pDB.get()) return;
7557 if (NULL == m_pDS.get()) return;
7559 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7560 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());
7562 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());
7564 m_pDS->query( strSQL.c_str() );
7566 while (!m_pDS->eof())
7568 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7569 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7575 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7576 CStdString path = StringUtils::Format("videodb://movies/titles/%ld", m_pDS->fv(0).get_asInt());
7577 pItem->SetPath(path);
7578 pItem->m_bIsFolder=false;
7588 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7592 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7598 if (NULL == m_pDB.get()) return;
7599 if (NULL == m_pDS.get()) return;
7601 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7602 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());
7604 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());
7606 m_pDS->query( strSQL.c_str() );
7608 while (!m_pDS->eof())
7610 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7611 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7617 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7618 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7620 pItem->SetPath("videodb://movies/directors/"+ strDir);
7621 pItem->m_bIsFolder=true;
7629 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7633 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7639 if (NULL == m_pDB.get()) return;
7640 if (NULL == m_pDS.get()) return;
7642 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7643 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());
7645 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());
7647 m_pDS->query( strSQL.c_str() );
7649 while (!m_pDS->eof())
7651 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7652 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7658 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7659 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7661 pItem->SetPath("videodb://tvshows/studios/"+ strDir);
7662 pItem->m_bIsFolder=true;
7670 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7674 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7680 if (NULL == m_pDB.get()) return;
7681 if (NULL == m_pDS.get()) return;
7683 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7684 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());
7686 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());
7688 m_pDS->query( strSQL.c_str() );
7690 while (!m_pDS->eof())
7692 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7693 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7699 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7700 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7702 pItem->SetPath("videodb://musicvideos/albums/"+ strDir);
7703 pItem->m_bIsFolder=true;
7711 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7715 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7717 CGUIDialogProgress *progress=NULL;
7720 if (NULL == m_pDB.get()) return;
7721 if (NULL == m_pDS.get()) return;
7723 unsigned int time = XbmcThreads::SystemClockMillis();
7724 CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
7725 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
7729 // find all the files
7730 CStdString sql = "SELECT files.idFile, files.strFileName, path.strPath FROM files, path WHERE files.idPath = path.idPath";
7735 RollbackTransaction();
7736 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7740 CStdString strPaths;
7741 for (std::set<int>::const_iterator it = paths->begin(); it != paths->end(); ++it)
7742 strPaths += StringUtils::Format(",%i", *it);
7743 sql += PrepareSQL(" AND path.idPath IN (%s)", strPaths.substr(1).c_str());
7746 m_pDS->query(sql.c_str());
7747 if (m_pDS->num_rows() == 0) return;
7751 handle->SetTitle(g_localizeStrings.Get(700));
7752 handle->SetText("");
7754 else if (showProgress)
7756 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
7759 progress->SetHeading(700);
7760 progress->SetLine(0, "");
7761 progress->SetLine(1, 313);
7762 progress->SetLine(2, 330);
7763 progress->SetPercentage(0);
7764 progress->StartModal();
7765 progress->ShowProgressBar(true);
7769 std::string filesToTestForDelete;
7771 int total = m_pDS->num_rows();
7774 while (!m_pDS->eof())
7776 std::string path = m_pDS->fv("path.strPath").get_asString();
7777 std::string fileName = m_pDS->fv("files.strFileName").get_asString();
7778 CStdString fullPath;
7779 ConstructPath(fullPath, path, fileName);
7781 // get the first stacked file
7782 if (URIUtils::IsStack(fullPath))
7783 fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
7785 // remove optical, non-existing files
7786 if (URIUtils::IsOnDVD(fullPath) || !CFile::Exists(fullPath, false))
7787 filesToTestForDelete += m_pDS->fv("files.idFile").get_asString() + ",";
7789 if (handle == NULL && progress != NULL)
7791 int percentage = current * 100 / total;
7792 if (percentage > progress->GetPercentage())
7794 progress->SetPercentage(percentage);
7795 progress->Progress();
7797 if (progress->IsCanceled())
7801 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7805 else if (handle != NULL)
7806 handle->SetPercentage(current * 100 / (float)total);
7813 std::string filesToDelete;
7815 // Add any files that don't have a valid idPath entry to the filesToDelete list.
7816 m_pDS->query("SELECT files.idFile FROM files WHERE NOT EXISTS (SELECT 1 FROM path WHERE path.idPath = files.idPath)");
7817 while (!m_pDS->eof())
7819 string file = m_pDS->fv("files.idFile").get_asString() + ",";
7820 filesToTestForDelete += file;
7821 filesToDelete += file;
7827 std::map<int, bool> pathsDeleteDecisions;
7828 std::vector<int> movieIDs;
7829 std::vector<int> tvshowIDs;
7830 std::vector<int> episodeIDs;
7831 std::vector<int> musicVideoIDs;
7833 if (!filesToTestForDelete.empty())
7835 StringUtils::TrimRight(filesToTestForDelete, ",");
7837 movieIDs = CleanMediaType("movie", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7838 episodeIDs = CleanMediaType("episode", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7839 musicVideoIDs = CleanMediaType("musicvideo", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7842 if (progress != NULL)
7844 progress->SetPercentage(100);
7845 progress->Progress();
7848 if (!filesToDelete.empty())
7850 filesToDelete = "(" + StringUtils::TrimRight(filesToDelete, ",") + ")";
7852 CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
7853 sql = "DELETE FROM files WHERE idFile IN " + filesToDelete;
7854 m_pDS->exec(sql.c_str());
7856 CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
7857 sql = "DELETE FROM streamdetails WHERE idFile IN " + filesToDelete;
7858 m_pDS->exec(sql.c_str());
7860 CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
7861 sql = "DELETE FROM bookmark WHERE idFile IN " + filesToDelete;
7862 m_pDS->exec(sql.c_str());
7864 CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
7865 sql = "DELETE FROM settings WHERE idFile IN " + filesToDelete;
7866 m_pDS->exec(sql.c_str());
7868 CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
7869 sql = "DELETE FROM stacktimes WHERE idFile IN " + filesToDelete;
7870 m_pDS->exec(sql.c_str());
7873 if (!movieIDs.empty())
7875 std::string moviesToDelete;
7876 for (std::vector<int>::const_iterator it = movieIDs.begin(); it != movieIDs.end(); ++it)
7877 moviesToDelete += StringUtils::Format("%i,", *it);
7878 moviesToDelete = "(" + StringUtils::TrimRight(moviesToDelete, ",") + ")";
7880 CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
7881 sql = "DELETE FROM movie WHERE idMovie IN " + moviesToDelete;
7882 m_pDS->exec(sql.c_str());
7884 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
7885 sql = "DELETE FROM actorlinkmovie WHERE idMovie IN " + moviesToDelete;
7886 m_pDS->exec(sql.c_str());
7888 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
7889 sql = "DELETE FROM directorlinkmovie WHERE idMovie IN " + moviesToDelete;
7890 m_pDS->exec(sql.c_str());
7892 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
7893 sql = "DELETE FROM writerlinkmovie WHERE idMovie IN " + moviesToDelete;
7894 m_pDS->exec(sql.c_str());
7896 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
7897 sql = "DELETE FROM genrelinkmovie WHERE idMovie IN " + moviesToDelete;
7898 m_pDS->exec(sql.c_str());
7900 CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
7901 sql = "DELETE FROM countrylinkmovie WHERE idMovie IN " + moviesToDelete;
7902 m_pDS->exec(sql.c_str());
7904 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
7905 sql = "DELETE FROM studiolinkmovie WHERE idMovie IN " + moviesToDelete;
7906 m_pDS->exec(sql.c_str());
7909 if (!episodeIDs.empty())
7911 std::string episodesToDelete;
7912 for (std::vector<int>::const_iterator it = episodeIDs.begin(); it != episodeIDs.end(); ++it)
7913 episodesToDelete += StringUtils::Format("%i,", *it);
7914 episodesToDelete = "(" + StringUtils::TrimRight(episodesToDelete, ",") + ")";
7916 CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
7917 sql = "DELETE FROM episode WHERE idEpisode IN " + episodesToDelete;
7918 m_pDS->exec(sql.c_str());
7920 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
7921 sql = "DELETE FROM actorlinkepisode WHERE idEpisode IN " + episodesToDelete;
7922 m_pDS->exec(sql.c_str());
7924 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
7925 sql = "DELETE FROM directorlinkepisode WHERE idEpisode IN " + episodesToDelete;
7926 m_pDS->exec(sql.c_str());
7928 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
7929 sql = "DELETE FROM writerlinkepisode WHERE idEpisode IN " + episodesToDelete;
7930 m_pDS->exec(sql.c_str());
7933 CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and have content set...", __FUNCTION__);
7934 sql = "SELECT path.idPath, path.strPath FROM path "
7935 "WHERE NOT ((strContent IS NULL OR strContent = '') "
7936 "AND (strSettings IS NULL OR strSettings = '') "
7937 "AND (strHash IS NULL OR strHash = '') "
7938 "AND (exclude IS NULL OR exclude != 1))";
7939 m_pDS->query(sql.c_str());
7941 while (!m_pDS->eof())
7943 std::map<int, bool>::const_iterator pathsDeleteDecision = pathsDeleteDecisions.find(m_pDS->fv(0).get_asInt());
7944 if ((pathsDeleteDecision != pathsDeleteDecisions.end() && pathsDeleteDecision->second) ||
7945 (pathsDeleteDecision == pathsDeleteDecisions.end() && !CDirectory::Exists(m_pDS->fv(1).get_asString(), false)))
7946 strIds += m_pDS->fv(0).get_asString() + ",";
7952 if (!strIds.empty())
7954 sql = PrepareSQL("DELETE FROM path WHERE idPath IN (%s)", StringUtils::TrimRight(strIds, ",").c_str());
7955 m_pDS->exec(sql.c_str());
7956 sql = "DELETE FROM tvshowlinkpath WHERE NOT EXISTS (SELECT 1 FROM path WHERE path.idPath = tvshowlinkpath.idPath)";
7957 m_pDS->exec(sql.c_str());
7960 CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
7961 sql = "DELETE FROM tvshow WHERE NOT EXISTS (SELECT 1 FROM tvshowlinkpath WHERE tvshowlinkpath.idShow = tvshow.idShow)";
7962 m_pDS->exec(sql.c_str());
7964 std::string tvshowsToDelete;
7965 sql = "SELECT tvshow.idShow FROM tvshow "
7966 "JOIN tvshowlinkpath ON tvshow.idShow = tvshowlinkpath.idShow "
7967 "JOIN path ON path.idPath = tvshowlinkpath.idPath "
7968 "WHERE NOT EXISTS (SELECT 1 FROM episode WHERE episode.idShow = tvshow.idShow) "
7969 "AND (path.strContent IS NULL OR path.strContent = '')";
7970 m_pDS->query(sql.c_str());
7971 while (!m_pDS->eof())
7973 tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
7974 tvshowsToDelete += m_pDS->fv(0).get_asString() + ",";
7978 if (!tvshowsToDelete.empty())
7980 sql = "DELETE FROM tvshow WHERE idShow IN (" + StringUtils::TrimRight(tvshowsToDelete, ",") + ")";
7981 m_pDS->exec(sql.c_str());
7983 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
7984 sql = "DELETE FROM actorlinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = actorlinktvshow.idShow)";
7985 m_pDS->exec(sql.c_str());
7987 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
7988 sql = "DELETE FROM directorlinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = directorlinktvshow.idShow)";
7989 m_pDS->exec(sql.c_str());
7991 CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
7992 sql = "DELETE FROM tvshowlinkpath WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = tvshowlinkpath.idShow)";
7993 m_pDS->exec(sql.c_str());
7995 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
7996 sql = "DELETE FROM genrelinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = genrelinktvshow.idShow)";
7997 m_pDS->exec(sql.c_str());
7999 CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
8000 sql = "DELETE FROM seasons WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = seasons.idShow)";
8001 m_pDS->exec(sql.c_str());
8003 CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
8004 sql = "DELETE FROM movielinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = movielinktvshow.idShow)";
8005 m_pDS->exec(sql.c_str());
8006 sql = "DELETE FROM movielinktvshow WHERE NOT EXISTS (SELECT 1 FROM movie WHERE movie.idMovie = movielinktvshow.idMovie)";
8007 m_pDS->exec(sql.c_str());
8010 if (!musicVideoIDs.empty())
8012 std::string musicVideosToDelete;
8013 for (std::vector<int>::const_iterator it = musicVideoIDs.begin(); it != musicVideoIDs.end(); ++it)
8014 musicVideosToDelete += StringUtils::Format("%i,", *it);
8015 musicVideosToDelete = "(" + StringUtils::TrimRight(musicVideosToDelete, ",") + ")";
8017 CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8018 sql = "DELETE FROM musicvideo WHERE idMVideo IN " + musicVideosToDelete;
8019 m_pDS->exec(sql.c_str());
8021 CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8022 sql = "DELETE FROM artistlinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8023 m_pDS->exec(sql.c_str());
8025 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8026 sql = "DELETE FROM directorlinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8027 m_pDS->exec(sql.c_str());
8029 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8030 sql = "DELETE FROM genrelinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8031 m_pDS->exec(sql.c_str());
8033 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8034 sql = "DELETE FROM studiolinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8035 m_pDS->exec(sql.c_str());
8038 CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8039 sql = StringUtils::Format("DELETE FROM path "
8040 "WHERE (strContent IS NULL OR strContent = '') "
8041 "AND (strSettings IS NULL OR strSettings = '') "
8042 "AND (strHash IS NULL OR strHash = '') "
8043 "AND (exclude IS NULL OR exclude != 1) "
8044 "AND NOT EXISTS (SELECT 1 FROM files WHERE files.idPath = path.idPath) "
8045 "AND NOT EXISTS (SELECT 1 FROM tvshowlinkpath WHERE tvshowlinkpath.idPath = path.idPath) "
8046 "AND NOT EXISTS (SELECT 1 FROM movie WHERE movie.c%02d = path.idPath) "
8047 "AND NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.c%02d = path.idPath) "
8048 "AND NOT EXISTS (SELECT 1 FROM episode WHERE episode.c%02d = path.idPath) "
8049 "AND NOT EXISTS (SELECT 1 FROM musicvideo WHERE musicvideo.c%02d = path.idPath)"
8050 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8051 m_pDS->exec(sql.c_str());
8053 CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8054 sql = "DELETE FROM genre "
8055 "WHERE NOT EXISTS (SELECT 1 FROM genrelinkmovie WHERE genrelinkmovie.idGenre = genre.idGenre) "
8056 "AND NOT EXISTS (SELECT 1 FROM genrelinktvshow WHERE genrelinktvshow.idGenre = genre.idGenre) "
8057 "AND NOT EXISTS (SELECT 1 FROM genrelinkmusicvideo WHERE genrelinkmusicvideo.idGenre = genre.idGenre)";
8058 m_pDS->exec(sql.c_str());
8060 CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8061 sql = "DELETE FROM country WHERE NOT EXISTS (SELECT 1 FROM countrylinkmovie WHERE countrylinkmovie.idCountry = country.idCountry)";
8062 m_pDS->exec(sql.c_str());
8064 CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8065 sql = "DELETE FROM actors "
8066 "WHERE NOT EXISTS (SELECT 1 FROM actorlinkmovie WHERE actorlinkmovie.idActor = actors.idActor) "
8067 "AND NOT EXISTS (SELECT 1 FROM directorlinkmovie WHERE directorlinkmovie.idDirector = actors.idActor) "
8068 "AND NOT EXISTS (SELECT 1 FROM writerlinkmovie WHERE writerlinkmovie.idWriter = actors.idActor) "
8069 "AND NOT EXISTS (SELECT 1 FROM actorlinktvshow WHERE actorlinktvshow.idActor = actors.idActor) "
8070 "AND NOT EXISTS (SELECT 1 FROM actorlinkepisode WHERE actorlinkepisode.idActor = actors.idActor) "
8071 "AND NOT EXISTS (SELECT 1 FROM directorlinktvshow WHERE directorlinktvshow.idDirector = actors.idActor) "
8072 "AND NOT EXISTS (SELECT 1 FROM directorlinkepisode WHERE directorlinkepisode.idDirector = actors.idActor) "
8073 "AND NOT EXISTS (SELECT 1 FROM writerlinkepisode WHERE writerlinkepisode.idWriter = actors.idActor) "
8074 "AND NOT EXISTS (SELECT 1 FROM artistlinkmusicvideo WHERE artistlinkmusicvideo.idArtist = actors.idActor) "
8075 "AND NOT EXISTS (SELECT 1 FROM directorlinkmusicvideo WHERE directorlinkmusicvideo.idDirector = actors.idActor)";
8076 m_pDS->exec(sql.c_str());
8078 CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8079 sql = "DELETE FROM studio "
8080 "WHERE NOT EXISTS (SELECT 1 FROM studiolinkmovie WHERE studiolinkmovie.idStudio = studio.idStudio) "
8081 "AND NOT EXISTS (SELECT 1 FROM studiolinkmusicvideo WHERE studiolinkmusicvideo.idStudio = studio.idStudio) "
8082 "AND NOT EXISTS (SELECT 1 FROM studiolinktvshow WHERE studiolinktvshow.idStudio = studio.idStudio)";
8083 m_pDS->exec(sql.c_str());
8085 CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8086 sql = "DELETE FROM sets WHERE NOT EXISTS (SELECT 1 FROM movie WHERE movie.idSet = sets.idSet)";
8087 m_pDS->exec(sql.c_str());
8089 CommitTransaction();
8092 handle->SetTitle(g_localizeStrings.Get(331));
8096 CUtil::DeleteVideoDatabaseDirectoryCache();
8098 time = XbmcThreads::SystemClockMillis() - time;
8099 CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8101 for (std::vector<int>::const_iterator it = movieIDs.begin(); it != movieIDs.end(); ++it)
8102 AnnounceRemove("movie", *it);
8104 for (std::vector<int>::const_iterator it = episodeIDs.begin(); it != episodeIDs.end(); ++it)
8105 AnnounceRemove("episode", *it);
8107 for (std::vector<int>::const_iterator it = tvshowIDs.begin(); it != tvshowIDs.end(); ++it)
8108 AnnounceRemove("tvshow", *it);
8110 for (std::vector<int>::const_iterator it = musicVideoIDs.begin(); it != musicVideoIDs.end(); ++it)
8111 AnnounceRemove("musicvideo", *it);
8115 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8116 RollbackTransaction();
8121 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8124 std::vector<int> CVideoDatabase::CleanMediaType(const std::string &mediaType, const std::string &cleanableFileIDs,
8125 std::map<int, bool> &pathsDeleteDecisions, std::string &deletedFileIDs, bool silent)
8127 std::vector<int> cleanedIDs;
8128 if (mediaType.empty() || cleanableFileIDs.empty())
8131 std::string table = mediaType;
8132 std::string idField;
8133 std::string parentPathIdField;
8134 bool isEpisode = false;
8135 if (mediaType == "movie")
8137 idField = "idMovie";
8138 parentPathIdField = StringUtils::Format("%s.c%02d", table.c_str(), VIDEODB_ID_PARENTPATHID);
8140 else if (mediaType == "episode")
8142 idField = "idEpisode";
8143 parentPathIdField = StringUtils::Format("tvshow.c%02d", VIDEODB_ID_TV_PARENTPATHID);
8146 else if (mediaType == "musicvideo")
8148 idField = "idMVideo";
8149 parentPathIdField = StringUtils::Format("%s.c%02d", table.c_str(), VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
8154 // now grab them media items
8155 std::string sql = PrepareSQL("SELECT %s.%s, %s.idFile, %s, path.idPath, parentPath.strPath, parentPath.useFolderNames FROM %s "
8156 "JOIN files ON files.idFile = %s.idFile "
8157 "JOIN path ON path.idPath = files.idPath ",
8158 table.c_str(), idField.c_str(), table.c_str(), parentPathIdField.c_str(), table.c_str(),
8162 sql += "JOIN tvshow ON tvshow.idShow = episode.idShow ";
8164 sql += PrepareSQL("JOIN path as parentPath ON parentPath.idPath = %s "
8165 "WHERE %s.idFile IN (%s)",
8166 parentPathIdField.c_str(),
8167 table.c_str(), cleanableFileIDs.c_str());
8169 // map of parent path ID to boolean pair (if not exists and user choice)
8170 std::map<int, std::pair<bool, bool> > parentPathsDeleteDecisions;
8171 m_pDS->query(sql.c_str());
8172 while (!m_pDS->eof())
8174 int parentPathID = m_pDS->fv(2).get_asInt();
8175 std::map<int, std::pair<bool, bool> >::const_iterator parentPathsDeleteDecision = parentPathsDeleteDecisions.find(parentPathID);
8177 if (parentPathsDeleteDecision == parentPathsDeleteDecisions.end())
8179 std::string parentPath = m_pDS->fv(4).get_asString();
8180 bool parentPathNotExists = !CDirectory::Exists(parentPath, false);
8181 // if the parent path exists, the file will be deleted without asking
8182 // if the parent path doesn't exist, ask the user whether to remove all items it contained
8183 if (parentPathNotExists)
8185 // in silent mode assume that the files are just temporarily missing
8190 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
8191 if (pDialog != NULL)
8193 CURL parentUrl(parentPath);
8194 pDialog->SetHeading(15012);
8195 pDialog->SetText(StringUtils::Format(g_localizeStrings.Get(15013), parentUrl.GetWithoutUserDetails().c_str()));
8196 pDialog->SetChoice(0, 15015);
8197 pDialog->SetChoice(1, 15014);
8199 //send message and wait for user input
8200 ThreadMessage tMsg = { TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, (unsigned int)g_windowManager.GetActiveWindow() };
8201 CApplicationMessenger::Get().SendMessage(tMsg, true);
8203 del = !pDialog->IsConfirmed();
8208 parentPathsDeleteDecisions.insert(make_pair(parentPathID, make_pair(parentPathNotExists, del)));
8210 // the only reason not to delete the file is if the parent path doesn't
8211 // exist and the user decided to delete all the items it contained
8212 else if (parentPathsDeleteDecision->second.first &&
8213 !parentPathsDeleteDecision->second.second)
8216 if (m_pDS->fv(5).get_asBool())
8217 pathsDeleteDecisions.insert(make_pair(m_pDS->fv(3).get_asInt(), del));
8221 deletedFileIDs += m_pDS->fv(1).get_asString() + ",";
8222 cleanedIDs.push_back(m_pDS->fv(0).get_asInt());
8232 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8235 CFileItemList items;
8236 GetTvShowsByWhere("videodb://tvshows/titles/", "", items);
8237 CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8238 CDirectory::Create(showPath);
8239 for (int i = 0; i < items.Size(); i++)
8241 // create a folder in this directory
8242 CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8243 CStdString TVFolder = URIUtils::AddFileToFolder(showPath, showName);
8244 if (CDirectory::Create(TVFolder))
8245 { // right - grab the episodes and dump them as well
8246 CFileItemList episodes;
8247 Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8248 GetEpisodesByWhere("videodb://tvshows/titles/", filter, episodes);
8249 for (int i = 0; i < episodes.Size(); i++)
8251 CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8252 CStdString episode = StringUtils::Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8254 CStdString episodePath = URIUtils::AddFileToFolder(TVFolder, episode);
8256 if (file.OpenForWrite(episodePath))
8263 GetMoviesByWhere("videodb://movies/titles/", "", items);
8264 CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8265 CDirectory::Create(moviePath);
8266 for (int i = 0; i < items.Size(); i++)
8268 CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8269 CStdString movie = StringUtils::Format("%s.avi", tag->m_strTitle.c_str());
8271 if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8276 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8278 CGUIDialogProgress *progress=NULL;
8281 if (NULL == m_pDB.get()) return;
8282 if (NULL == m_pDS.get()) return;
8283 if (NULL == m_pDS2.get()) return;
8285 // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8286 auto_ptr<Dataset> pDS;
8287 pDS.reset(m_pDB->CreateDataset());
8288 if (NULL == pDS.get()) return;
8290 auto_ptr<Dataset> pDS2;
8291 pDS2.reset(m_pDB->CreateDataset());
8292 if (NULL == pDS2.get()) return;
8294 // if we're exporting to a single folder, we export thumbs as well
8295 CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8296 CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8297 CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8298 CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8299 CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8300 CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8306 CDirectory::Remove(exportRoot);
8307 CDirectory::Create(exportRoot);
8308 CDirectory::Create(actorsDir);
8309 CDirectory::Create(moviesDir);
8310 CDirectory::Create(musicvideosDir);
8311 CDirectory::Create(tvshowsDir);
8314 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8316 CStdString sql = "select * from movieview";
8318 m_pDS->query(sql.c_str());
8322 progress->SetHeading(647);
8323 progress->SetLine(0, 650);
8324 progress->SetLine(1, "");
8325 progress->SetLine(2, "");
8326 progress->SetPercentage(0);
8327 progress->StartModal();
8328 progress->ShowProgressBar(true);
8331 int total = m_pDS->num_rows();
8334 // create our xml document
8335 CXBMCTinyXML xmlDoc;
8336 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8337 xmlDoc.InsertEndChild(decl);
8338 TiXmlNode *pMain = NULL;
8343 TiXmlElement xmlMainElement("videodb");
8344 pMain = xmlDoc.InsertEndChild(xmlMainElement);
8345 XMLUtils::SetInt(pMain,"version", GetExportVersion());
8348 while (!m_pDS->eof())
8350 CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8351 // strip paths to make them relative
8352 if (StringUtils::StartsWith(movie.m_strTrailer, movie.m_strPath))
8353 movie.m_strTrailer = movie.m_strTrailer.substr(movie.m_strPath.size());
8354 map<string, string> artwork;
8355 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8357 TiXmlElement additionalNode("art");
8358 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8359 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8360 movie.Save(pMain, "movie", true, &additionalNode);
8363 movie.Save(pMain, "movie", !singleFiles);
8365 // reset old skip state
8370 progress->SetLine(1, movie.m_strTitle);
8371 progress->SetPercentage(current * 100 / total);
8372 progress->Progress();
8373 if (progress->IsCanceled())
8381 CFileItem item(movie.m_strFileNameAndPath,false);
8382 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8384 if (!item.Exists(false))
8386 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8391 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8393 if (item.IsOpticalMediaFile())
8395 nfoFile = URIUtils::AddFileToFolder(
8396 URIUtils::GetParentPath(nfoFile),
8397 URIUtils::GetFileName(nfoFile));
8400 if (overwrite || !CFile::Exists(nfoFile, false))
8402 if(!xmlDoc.SaveFile(nfoFile))
8404 CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8405 bSkip = ExportSkipEntry(nfoFile);
8422 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8423 xmlDoc.InsertEndChild(decl);
8426 if (images && !bSkip)
8430 CStdString strFileName(movie.m_strTitle);
8431 if (movie.m_iYear > 0)
8432 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8433 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8435 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8437 CStdString savedThumb = item.GetLocalArt(i->first, false);
8438 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8441 ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8448 // find all musicvideos
8449 sql = "select * from musicvideoview";
8451 m_pDS->query(sql.c_str());
8453 total = m_pDS->num_rows();
8456 while (!m_pDS->eof())
8458 CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8459 map<string, string> artwork;
8460 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8462 TiXmlElement additionalNode("art");
8463 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8464 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8465 movie.Save(pMain, "musicvideo", true, &additionalNode);
8468 movie.Save(pMain, "musicvideo", !singleFiles);
8470 // reset old skip state
8475 progress->SetLine(1, movie.m_strTitle);
8476 progress->SetPercentage(current * 100 / total);
8477 progress->Progress();
8478 if (progress->IsCanceled())
8486 CFileItem item(movie.m_strFileNameAndPath,false);
8487 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8489 if (!item.Exists(false))
8491 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8496 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8498 if (overwrite || !CFile::Exists(nfoFile, false))
8500 if(!xmlDoc.SaveFile(nfoFile))
8502 CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8503 bSkip = ExportSkipEntry(nfoFile);
8520 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8521 xmlDoc.InsertEndChild(decl);
8523 if (images && !bSkip)
8527 CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8528 if (movie.m_iYear > 0)
8529 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8530 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8532 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8534 CStdString savedThumb = item.GetLocalArt(i->first, false);
8535 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8543 // repeat for all tvshows
8544 sql = "SELECT * FROM tvshowview";
8545 m_pDS->query(sql.c_str());
8547 total = m_pDS->num_rows();
8550 while (!m_pDS->eof())
8552 CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8554 map<int, map<string, string> > seasonArt;
8555 GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8557 map<string, string> artwork;
8558 if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8560 TiXmlElement additionalNode("art");
8561 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8562 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8563 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8565 TiXmlElement seasonNode("season");
8566 seasonNode.SetAttribute("num", i->first);
8567 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8568 XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8569 additionalNode.InsertEndChild(seasonNode);
8571 tvshow.Save(pMain, "tvshow", true, &additionalNode);
8574 tvshow.Save(pMain, "tvshow", !singleFiles);
8576 // reset old skip state
8581 progress->SetLine(1, tvshow.m_strTitle);
8582 progress->SetPercentage(current * 100 / total);
8583 progress->Progress();
8584 if (progress->IsCanceled())
8592 // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8593 if (URIUtils::IsMultiPath(tvshow.m_strPath))
8594 tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8596 CFileItem item(tvshow.m_strPath, true);
8597 if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8599 if (!item.Exists(false))
8601 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8606 CStdString nfoFile = URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo");
8608 if (overwrite || !CFile::Exists(nfoFile, false))
8610 if(!xmlDoc.SaveFile(nfoFile))
8612 CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8613 bSkip = ExportSkipEntry(nfoFile);
8630 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8631 xmlDoc.InsertEndChild(decl);
8633 if (images && !bSkip)
8636 item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8638 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8640 CStdString savedThumb = item.GetLocalArt(i->first, true);
8641 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8645 ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8647 // export season thumbs
8648 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8652 seasonThumb = "season-all";
8653 else if (i->first == 0)
8654 seasonThumb = "season-specials";
8656 seasonThumb = StringUtils::Format("season%02i", i->first);
8657 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8659 CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8660 if (!i->second.empty())
8661 CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8666 // now save the episodes from this show
8667 sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8668 pDS->query(sql.c_str());
8669 CStdString showDir(item.GetPath());
8673 CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8674 map<string, string> artwork;
8675 if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8677 TiXmlElement additionalNode("art");
8678 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8679 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8680 episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8682 else if (!singleFiles)
8683 episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8685 episode.Save(pMain, "episodedetails", !singleFiles);
8687 // multi-episode files need dumping to the same XML
8688 while (singleFiles && !pDS->eof() &&
8689 episode.m_iFileId == pDS->fv("idFile").get_asInt())
8691 episode = GetDetailsForEpisode(pDS, true);
8692 episode.Save(pMain, "episodedetails", !singleFiles);
8696 // reset old skip state
8699 CFileItem item(episode.m_strFileNameAndPath, false);
8700 if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8702 if (!item.Exists(false))
8704 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8709 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8711 if (overwrite || !CFile::Exists(nfoFile, false))
8713 if(!xmlDoc.SaveFile(nfoFile))
8715 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8716 bSkip = ExportSkipEntry(nfoFile);
8733 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8734 xmlDoc.InsertEndChild(decl);
8737 if (images && !bSkip)
8741 CStdString epName = StringUtils::Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8742 item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8744 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8746 CStdString savedThumb = item.GetLocalArt(i->first, false);
8747 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8750 ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8759 if (singleFiles && progress)
8761 progress->SetPercentage(100);
8762 progress->Progress();
8767 // now dump path info
8768 set<CStdString> paths;
8770 TiXmlElement xmlPathElement("paths");
8771 TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8772 for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8774 bool foundDirectly = false;
8775 SScanSettings settings;
8776 ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8777 if (info && foundDirectly)
8779 TiXmlElement xmlPathElement2("path");
8780 TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8781 XMLUtils::SetString(pPath,"url", *iter);
8782 XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8783 XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8784 XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8785 XMLUtils::SetString(pPath,"scraperpath", info->ID());
8788 xmlDoc.SaveFile(xmlFile);
8793 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8800 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8802 CStdString strPath(strDir);
8805 strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8806 if (!CDirectory::Exists(strPath))
8808 CDirectory::Create(strPath);
8809 CFile::SetHidden(strPath, true);
8813 for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8816 item.SetLabel(iter->strName);
8817 if (!iter->thumb.empty())
8819 CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8820 CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8825 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8827 CStdString strParent;
8828 URIUtils::GetParentPath(nfoFile,strParent);
8829 CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8831 bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8834 CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8836 CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8841 void CVideoDatabase::ImportFromXML(const CStdString &path)
8843 CGUIDialogProgress *progress=NULL;
8846 if (NULL == m_pDB.get()) return;
8847 if (NULL == m_pDS.get()) return;
8849 CXBMCTinyXML xmlDoc;
8850 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
8853 TiXmlElement *root = xmlDoc.RootElement();
8856 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8859 progress->SetHeading(648);
8860 progress->SetLine(0, 649);
8861 progress->SetLine(1, 330);
8862 progress->SetLine(2, "");
8863 progress->SetPercentage(0);
8864 progress->StartModal();
8865 progress->ShowProgressBar(true);
8869 XMLUtils::GetInt(root, "version", iVersion);
8871 CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
8873 TiXmlElement *movie = root->FirstChildElement();
8876 // first count the number of items...
8879 if (strnicmp(movie->Value(), "movie", 5)==0 ||
8880 strnicmp(movie->Value(), "tvshow", 6)==0 ||
8881 strnicmp(movie->Value(), "musicvideo",10)==0 )
8883 movie = movie->NextSiblingElement();
8886 CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
8887 CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
8888 CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
8889 CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
8890 CVideoInfoScanner scanner;
8891 // add paths first (so we have scraper settings available)
8892 TiXmlElement *path = root->FirstChildElement("paths");
8893 path = path->FirstChildElement();
8897 if (XMLUtils::GetString(path,"url",strPath) && !strPath.empty())
8901 if (XMLUtils::GetString(path,"content", content) && !content.empty())
8902 { // check the scraper exists, if so store the path
8905 XMLUtils::GetString(path,"scraperpath",id);
8906 if (CAddonMgr::Get().GetAddon(id, addon))
8908 SScanSettings settings;
8909 ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
8910 // FIXME: scraper settings are not exported?
8911 scraper->SetPathSettings(TranslateContent(content), "");
8912 XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
8913 XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
8914 SetScraperForPath(strPath,scraper,settings);
8917 path = path->NextSiblingElement();
8919 movie = root->FirstChildElement();
8923 if (strnicmp(movie->Value(), "movie", 5) == 0)
8926 CFileItem item(info);
8927 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
8928 CStdString filename = info.m_strTitle;
8929 if (info.m_iYear > 0)
8930 filename += StringUtils::Format("_%i", info.m_iYear);
8931 CFileItem artItem(item);
8932 artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
8933 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8934 item.SetArt(artItem.GetArt());
8935 scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
8938 else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
8941 CFileItem item(info);
8942 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
8943 CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
8944 if (info.m_iYear > 0)
8945 filename += StringUtils::Format("_%i", info.m_iYear);
8946 CFileItem artItem(item);
8947 artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
8948 scanner.GetArtwork(&artItem, CONTENT_MUSICVIDEOS, useFolders, true, actorsDir);
8949 item.SetArt(artItem.GetArt());
8950 scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
8953 else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
8955 // load the TV show in. NOTE: This deletes all episodes under the TV Show, which may not be
8956 // what we desire. It may make better sense to only delete (or even better, update) the show information
8958 URIUtils::AddSlashAtEnd(info.m_strPath);
8959 DeleteTvShow(info.m_strPath);
8960 CFileItem showItem(info);
8961 bool useFolders = info.m_basePath.empty() ? LookupByFolders(showItem.GetPath(), true) : false;
8962 CFileItem artItem(showItem);
8963 CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
8964 artItem.SetPath(artPath);
8965 scanner.GetArtwork(&artItem, CONTENT_TVSHOWS, useFolders, true, actorsDir);
8966 showItem.SetArt(artItem.GetArt());
8967 int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
8969 map<int, map<string, string> > seasonArt;
8970 artItem.GetVideoInfoTag()->m_strPath = artPath;
8971 scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
8972 for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8974 int seasonID = AddSeason(showID, i->first);
8975 SetArtForItem(seasonID, "season", i->second);
8978 // now load the episodes
8979 TiXmlElement *episode = movie->FirstChildElement("episodedetails");
8982 // no need to delete the episode info, due to the above deletion
8985 CFileItem item(info);
8986 CStdString filename = StringUtils::Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
8987 CFileItem artItem(item);
8988 artItem.SetPath(GetSafeFile(artPath, filename));
8989 scanner.GetArtwork(&artItem, CONTENT_TVSHOWS, useFolders, true, actorsDir);
8990 item.SetArt(artItem.GetArt());
8991 scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
8992 episode = episode->NextSiblingElement("episodedetails");
8995 movie = movie->NextSiblingElement();
8996 if (progress && total)
8998 progress->SetPercentage(current * 100 / total);
8999 progress->SetLine(2, info.m_strTitle);
9000 progress->Progress();
9001 if (progress->IsCanceled())
9011 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
9017 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
9019 if (!node) return false;
9020 const TiXmlNode *art = node->FirstChild();
9021 while (art && art->FirstChild())
9023 artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
9024 art = art->NextSibling();
9026 return !artwork.empty();
9029 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
9031 if (URIUtils::IsStack(strFileName) ||
9032 URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
9033 strDest = strFileName;
9035 strDest = URIUtils::AddFileToFolder(strPath, strFileName);
9038 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
9040 if (URIUtils::IsStack(strFileNameAndPath) || StringUtils::StartsWithNoCase(strFileNameAndPath, "rar://") || StringUtils::StartsWithNoCase(strFileNameAndPath, "zip://"))
9042 URIUtils::GetParentPath(strFileNameAndPath,strPath);
9043 strFileName = strFileNameAndPath;
9045 else if (URIUtils::IsPlugin(strFileNameAndPath))
9047 CURL url(strFileNameAndPath);
9048 strPath = url.GetWithoutFilename();
9049 strFileName = strFileNameAndPath;
9052 URIUtils::Split(strFileNameAndPath,strPath, strFileName);
9055 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
9057 SScanSettings settings;
9059 ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
9060 SetPathHash(strPath,"");
9063 if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
9065 if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
9067 CStdString strParent;
9068 URIUtils::GetParentPath(strPath,strParent);
9069 SetPathHash(strParent,"");
9074 bool CVideoDatabase::CommitTransaction()
9076 if (CDatabase::CommitTransaction())
9077 { // number of items in the db has likely changed, so recalculate
9078 g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
9079 g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
9080 g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
9086 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, int dbField, const std::string &strValue)
9091 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9094 string strTable, strField;
9095 if (type == VIDEODB_CONTENT_MOVIES)
9098 strField = "idMovie";
9100 else if (type == VIDEODB_CONTENT_TVSHOWS)
9102 strTable = "tvshow";
9103 strField = "idShow";
9105 else if (type == VIDEODB_CONTENT_EPISODES)
9107 strTable = "episode";
9108 strField = "idEpisode";
9110 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
9112 strTable = "musicvideo";
9113 strField = "idMVideo";
9116 if (strTable.empty())
9119 return SetSingleValue(strTable, StringUtils::Format("c%02u", dbField), strValue, strField, dbId);
9123 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
9128 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, Field dbField, const std::string &strValue)
9130 MediaType mediaType = DatabaseUtils::MediaTypeFromVideoContentType(type);
9131 if (mediaType == MediaTypeNone)
9134 int dbFieldIndex = DatabaseUtils::GetField(dbField, mediaType);
9135 if (dbFieldIndex < 0)
9138 return SetSingleValue(type, dbId, dbFieldIndex, strValue);
9141 bool CVideoDatabase::SetSingleValue(const std::string &table, const std::string &fieldName, const std::string &strValue,
9142 const std::string &conditionName /* = "" */, int conditionValue /* = -1 */)
9144 if (table.empty() || fieldName.empty())
9150 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9153 sql = PrepareSQL("UPDATE %s SET %s='%s'", table.c_str(), fieldName.c_str(), strValue.c_str());
9154 if (!conditionName.empty())
9155 sql += PrepareSQL(" WHERE %s=%u", conditionName.c_str(), conditionValue);
9156 if (m_pDS->exec(sql.c_str()) == 0)
9161 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, sql.c_str());
9166 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
9168 CStdString safeThumb(name);
9169 StringUtils::Replace(safeThumb, ' ', '_');
9170 return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
9173 void CVideoDatabase::AnnounceRemove(std::string content, int id)
9176 data["type"] = content;
9178 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9181 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9184 data["type"] = content;
9186 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9189 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9191 CStdString path(strPath);
9193 if(URIUtils::IsMultiPath(path))
9195 vector<CStdString> paths;
9196 CMultiPathDirectory::GetPaths(path, paths);
9198 for(unsigned i=0;i<paths.size();i++)
9199 GetItemsForPath(content, paths[i], items);
9201 return items.Size() > 0;
9204 int pathID = GetPathId(path);
9208 if (content == "movies")
9210 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9211 GetMoviesByWhere("videodb://movies/titles/", filter, items);
9213 else if (content == "episodes")
9215 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9216 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
9218 else if (content == "tvshows")
9220 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9221 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
9223 else if (content == "musicvideos")
9225 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9226 GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
9228 for (int i = 0; i < items.Size(); i++)
9229 items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9230 return items.Size() > 0;
9233 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9235 if (!videoUrl.IsValid())
9238 std::string type = videoUrl.GetType();
9239 std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9240 const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9241 CUrlOptions::UrlOptions::const_iterator option;
9243 if (type == "movies")
9245 option = options.find("genreid");
9246 if (option != options.end())
9248 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9249 filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9252 option = options.find("genre");
9253 if (option != options.end())
9255 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9256 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9259 option = options.find("countryid");
9260 if (option != options.end())
9262 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9263 filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9266 option = options.find("country");
9267 if (option != options.end())
9269 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9270 filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9273 option = options.find("studioid");
9274 if (option != options.end())
9276 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9277 filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9280 option = options.find("studio");
9281 if (option != options.end())
9283 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9284 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9287 option = options.find("directorid");
9288 if (option != options.end())
9290 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9291 filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9294 option = options.find("director");
9295 if (option != options.end())
9297 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9298 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9301 option = options.find("year");
9302 if (option != options.end())
9303 filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9305 option = options.find("actorid");
9306 if (option != options.end())
9308 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9309 filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9312 option = options.find("actor");
9313 if (option != options.end())
9315 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9316 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9319 option = options.find("setid");
9320 if (option != options.end())
9321 filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9323 option = options.find("set");
9324 if (option != options.end())
9326 filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9327 filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9330 option = options.find("tagid");
9331 if (option != options.end())
9333 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9334 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9337 option = options.find("tag");
9338 if (option != options.end())
9340 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9341 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9344 else if (type == "tvshows")
9346 if (itemType == "tvshows")
9348 option = options.find("genreid");
9349 if (option != options.end())
9351 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9352 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9355 option = options.find("genre");
9356 if (option != options.end())
9358 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9359 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9362 option = options.find("studioid");
9363 if (option != options.end())
9365 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9366 filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9369 option = options.find("studio");
9370 if (option != options.end())
9372 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9373 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9376 option = options.find("directorid");
9377 if (option != options.end())
9379 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9380 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9383 option = options.find("year");
9384 if (option != options.end())
9385 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9387 option = options.find("actorid");
9388 if (option != options.end())
9390 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9391 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9394 option = options.find("actor");
9395 if (option != options.end())
9397 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9398 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9401 option = options.find("tagid");
9402 if (option != options.end())
9404 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9405 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9408 option = options.find("tag");
9409 if (option != options.end())
9411 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9412 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9415 else if (itemType == "seasons")
9417 option = options.find("genreid");
9418 if (option != options.end())
9420 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9421 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9424 option = options.find("directorid");
9425 if (option != options.end())
9427 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9428 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9431 option = options.find("year");
9432 if (option != options.end())
9433 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9435 option = options.find("actorid");
9436 if (option != options.end())
9438 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9439 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9442 else if (itemType == "episodes")
9445 option = options.find("tvshowid");
9446 if (option != options.end())
9447 idShow = (int)option->second.asInteger();
9450 option = options.find("season");
9451 if (option != options.end())
9452 season = (int)option->second.asInteger();
9454 CStdString strIn = PrepareSQL("= %i", idShow);
9455 GetStackedTvShowList(idShow, strIn);
9459 bool condition = false;
9461 option = options.find("genreid");
9462 if (option != options.end())
9465 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9466 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9469 option = options.find("genre");
9470 if (option != options.end())
9473 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9474 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9477 option = options.find("directorid");
9478 if (option != options.end())
9481 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9482 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9485 option = options.find("director");
9486 if (option != options.end())
9489 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9490 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9493 option = options.find("year");
9494 if (option != options.end())
9497 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9500 option = options.find("actorid");
9501 if (option != options.end())
9504 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9505 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9508 option = options.find("actor");
9509 if (option != options.end())
9512 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9513 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9517 filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9521 if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9522 filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9524 filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9525 VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9530 option = options.find("year");
9531 if (option != options.end())
9532 filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9534 option = options.find("directorid");
9535 if (option != options.end())
9537 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9538 filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9541 option = options.find("director");
9542 if (option != options.end())
9544 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9545 filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9550 else if (type == "musicvideos")
9552 option = options.find("genreid");
9553 if (option != options.end())
9555 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9556 filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9559 option = options.find("genre");
9560 if (option != options.end())
9562 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9563 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9566 option = options.find("studioid");
9567 if (option != options.end())
9569 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9570 filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9573 option = options.find("studio");
9574 if (option != options.end())
9576 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9577 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9580 option = options.find("directorid");
9581 if (option != options.end())
9583 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9584 filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9587 option = options.find("director");
9588 if (option != options.end())
9590 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9591 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9594 option = options.find("year");
9595 if (option != options.end())
9596 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9598 option = options.find("artistid");
9599 if (option != options.end())
9601 if (itemType != "albums")
9602 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9603 filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9606 option = options.find("artist");
9607 if (option != options.end())
9609 if (itemType != "albums")
9611 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9612 filter.AppendJoin(PrepareSQL("join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9614 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9617 option = options.find("albumid");
9618 if (option != options.end())
9619 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()));
9621 option = options.find("tagid");
9622 if (option != options.end())
9624 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9625 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9628 option = options.find("tag");
9629 if (option != options.end())
9631 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9632 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9638 option = options.find("xsp");
9639 if (option != options.end())
9642 if (!xsp.LoadFromJson(option->second.asString()))
9645 // check if the filter playlist matches the item type
9646 if (xsp.GetType() == itemType ||
9647 (xsp.GetGroup() == itemType && !xsp.IsGroupMixed()) ||
9648 // handle episode listings with videodb://tvshows/titles/ which get the rest
9649 // of the path (season and episodeid) appended later
9650 (xsp.GetType() == "episodes" && itemType == "tvshows"))
9652 std::set<CStdString> playlists;
9653 filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9655 if (xsp.GetLimit() > 0)
9656 sorting.limitEnd = xsp.GetLimit();
9657 if (xsp.GetOrder() != SortByNone)
9658 sorting.sortBy = xsp.GetOrder();
9659 if (xsp.GetOrderDirection() != SortOrderNone)
9660 sorting.sortOrder = xsp.GetOrderDirection();
9661 if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
9662 sorting.sortAttributes = SortAttributeIgnoreArticle;
9666 option = options.find("filter");
9667 if (option != options.end())
9669 CSmartPlaylist xspFilter;
9670 if (!xspFilter.LoadFromJson(option->second.asString()))
9673 // check if the filter playlist matches the item type
9674 if (xspFilter.GetType() == itemType)
9676 std::set<CStdString> playlists;
9677 filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9679 // remove the filter if it doesn't match the item type
9681 videoUrl.RemoveOption("filter");