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 // query DB for any episodes matching idShow, Season and Episode
2296 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);
2297 m_pDS->query(strSQL.c_str());
2301 int playCount = m_pDS->fv("files.playCount").get_asInt();
2303 CDateTime lastPlayed;
2304 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2306 int idFile = GetFileId(strFilenameAndPath);
2308 // update with playCount and lastPlayed
2309 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2310 m_pDS->exec(strSQL.c_str());
2315 // and insert the new row
2316 CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
2317 sql += PrepareSQL(" where idEpisode=%i", idEpisode);
2318 m_pDS->exec(sql.c_str());
2319 CommitTransaction();
2325 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2327 RollbackTransaction();
2331 int CVideoDatabase::GetSeasonId(int showID, int season)
2333 CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
2334 CStdString id = GetSingleValue("seasons", "idSeason", sql);
2337 return strtol(id.c_str(), NULL, 10);
2340 int CVideoDatabase::AddSeason(int showID, int season)
2342 int seasonId = GetSeasonId(showID, season);
2345 if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
2346 seasonId = (int)m_pDS->lastinsertid();
2351 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
2358 idMVideo = GetMusicVideoId(strFilenameAndPath);
2361 DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
2364 // only add a new musicvideo if we don't already have a valid idMVideo
2365 // (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
2366 // be removed from the musicvideo table)
2367 idMVideo = AddMusicVideo(strFilenameAndPath);
2370 RollbackTransaction();
2375 vector<int> vecDirectors;
2376 vector<int> vecGenres;
2377 vector<int> vecStudios;
2378 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2381 if (!details.m_artist.empty())
2383 for (unsigned int i = 0; i < details.m_artist.size(); i++)
2385 CStdString artist = details.m_artist[i];
2386 StringUtils::Trim(artist);
2387 int idArtist = AddActor(artist,"");
2388 AddArtistToMusicVideo(idMVideo, idArtist);
2393 for (i = 0; i < vecGenres.size(); ++i)
2395 AddGenreToMusicVideo(idMVideo, vecGenres[i]);
2398 for (i = 0; i < vecDirectors.size(); ++i)
2400 AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
2403 for (i = 0; i < vecStudios.size(); ++i)
2405 AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2409 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2411 int idTag = AddTag(details.m_tags[i]);
2412 AddTagToItem(idMVideo, idTag, "musicvideo");
2415 if (details.HasStreamDetails())
2416 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2418 SetArtForItem(idMVideo, "musicvideo", artwork);
2420 // update our movie table (we know it was added already above)
2421 // and insert the new row
2422 CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
2423 sql += PrepareSQL(" where idMVideo=%i", idMVideo);
2424 m_pDS->exec(sql.c_str());
2425 CommitTransaction();
2431 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2433 RollbackTransaction();
2437 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2439 // AddFile checks to make sure the file isn't already in the DB first
2440 int idFile = AddFile(strFileNameAndPath);
2443 SetStreamDetailsForFileId(details, idFile);
2446 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2454 m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2456 for (int i=1; i<=details.GetVideoStreamCount(); i++)
2458 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2459 "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration, strStereoMode) "
2460 "VALUES (%i,%i,'%s',%f,%i,%i,%i,'%s')",
2461 idFile, (int)CStreamDetail::VIDEO,
2462 details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2463 details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i),
2464 details.GetStereoMode(i).c_str()));
2466 for (int i=1; i<=details.GetAudioStreamCount(); i++)
2468 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2469 "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2470 "VALUES (%i,%i,'%s',%i,'%s')",
2471 idFile, (int)CStreamDetail::AUDIO,
2472 details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2473 details.GetAudioLanguage(i).c_str()));
2475 for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2477 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2478 "(idFile, iStreamType, strSubtitleLanguage) "
2479 "VALUES (%i,%i,'%s')",
2480 idFile, (int)CStreamDetail::SUBTITLE,
2481 details.GetSubtitleLanguage(i).c_str()));
2484 // update the runtime information, if empty
2485 if (details.GetVideoDuration())
2487 vector< pair<string, int> > tables;
2488 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
2489 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
2490 tables.push_back(make_pair("musicvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
2491 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
2493 CStdString sql = PrepareSQL("update %s set c%02d=%d where idFile=%d and c%02d=''",
2494 i->first.c_str(), i->second, details.GetVideoDuration(), idFile, i->second);
2499 CommitTransaction();
2503 RollbackTransaction();
2504 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2508 //********************************************************************************************************************************
2509 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2513 if (NULL == m_pDB.get()) return ;
2514 if (NULL == m_pDS.get()) return ;
2516 if (idMovie < 0) return ;
2519 if (iType == VIDEODB_CONTENT_MOVIES)
2520 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 );
2521 if (iType == VIDEODB_CONTENT_EPISODES)
2522 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 );
2523 if (iType == VIDEODB_CONTENT_TVSHOWS)
2524 strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2525 if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2526 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 );
2528 m_pDS->query( strSQL.c_str() );
2531 if (iType != VIDEODB_CONTENT_TVSHOWS)
2533 CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2534 ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2537 filePath = m_pDS->fv("path.strPath").get_asString();
2543 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2547 //********************************************************************************************************************************
2548 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend, long partNumber)
2552 if (URIUtils::IsStack(strFilenameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(strFilenameAndPath),false).IsDVDImage())
2554 CStackDirectory dir;
2555 CFileItemList fileList;
2556 dir.GetDirectory(strFilenameAndPath, fileList);
2559 for (int i = fileList.Size() - 1; i >= 0; i--) // put the bookmarks of the highest part first in the list
2560 GetBookMarksForFile(fileList[i]->GetPath(), bookmarks, type, true, (i+1));
2564 int idFile = GetFileId(strFilenameAndPath);
2565 if (idFile < 0) return ;
2567 bookmarks.erase(bookmarks.begin(), bookmarks.end());
2568 if (NULL == m_pDB.get()) return ;
2569 if (NULL == m_pDS.get()) return ;
2571 CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2572 m_pDS->query( strSQL.c_str() );
2573 while (!m_pDS->eof())
2576 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2577 bookmark.partNumber = partNumber;
2578 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2579 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2580 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2581 bookmark.player = m_pDS->fv("player").get_asString();
2582 bookmark.type = type;
2583 if (type == CBookmark::EPISODE)
2585 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);
2586 m_pDS2->query(strSQL2.c_str());
2587 bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2588 bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2591 bookmarks.push_back(bookmark);
2594 //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2600 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2604 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2606 VECBOOKMARKS bookmarks;
2607 GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2608 if (bookmarks.size() > 0)
2610 bookmark = bookmarks[0];
2616 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2618 if (!m_pDB.get() || !m_pDS.get())
2621 int fileID = GetFileId(strFilenameAndPath);
2627 CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2628 m_pDS->exec(sql.c_str());
2632 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2636 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2640 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);
2641 m_pDS->query(strSQL.c_str());
2642 while (!m_pDS->eof())
2644 episodes.push_back(GetDetailsForEpisode(m_pDS));
2651 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2655 //********************************************************************************************************************************
2656 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2660 int idFile = AddFile(strFilenameAndPath);
2663 if (NULL == m_pDB.get()) return ;
2664 if (NULL == m_pDS.get()) return ;
2668 if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2670 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2672 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
2674 /* get a bookmark within the same time as previous */
2675 double mintime = bookmark.timeInSeconds - 0.5f;
2676 double maxtime = bookmark.timeInSeconds + 0.5f;
2677 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());
2680 if (type != CBookmark::EPISODE)
2683 m_pDS->query( strSQL.c_str() );
2684 if (m_pDS->num_rows() != 0)
2685 idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2688 // update or insert depending if it existed before
2689 if (idBookmark >= 0 )
2690 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);
2692 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);
2694 m_pDS->exec(strSQL.c_str());
2698 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2702 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2706 int idFile = GetFileId(strFilenameAndPath);
2707 if (idFile < 0) return ;
2708 if (NULL == m_pDB.get()) return ;
2709 if (NULL == m_pDS.get()) return ;
2711 /* a litle bit uggly, we clear first bookmark that is within one second of given */
2712 /* should be no problem since we never add bookmarks that are closer than that */
2713 double mintime = bookmark.timeInSeconds - 0.5f;
2714 double maxtime = bookmark.timeInSeconds + 0.5f;
2715 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);
2717 m_pDS->query( strSQL.c_str() );
2718 if (m_pDS->num_rows() != 0)
2720 int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2721 strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2722 m_pDS->exec(strSQL.c_str());
2723 if (type == CBookmark::EPISODE)
2725 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);
2726 m_pDS->exec(strSQL.c_str());
2734 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2738 //********************************************************************************************************************************
2739 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2743 int idFile = GetFileId(strFilenameAndPath);
2744 if (idFile < 0) return ;
2745 if (NULL == m_pDB.get()) return ;
2746 if (NULL == m_pDS.get()) return ;
2748 CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2749 m_pDS->exec(strSQL.c_str());
2750 if (type == CBookmark::EPISODE)
2752 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2753 m_pDS->exec(strSQL.c_str());
2758 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2763 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2767 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);
2768 m_pDS->query( strSQL.c_str() );
2771 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2772 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2773 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2774 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2775 bookmark.player = m_pDS->fv("player").get_asString();
2776 bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2787 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2793 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2797 int idFile = GetFileId(tag.m_strFileNameAndPath);
2798 // delete the current episode for the selected episode number
2799 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);
2800 m_pDS->exec(strSQL.c_str());
2802 AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2803 int idBookmark = (int)m_pDS->lastinsertid();
2804 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);
2805 m_pDS->exec(strSQL.c_str());
2809 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2813 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2817 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2818 m_pDS->exec(strSQL.c_str());
2819 strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2820 m_pDS->exec(strSQL.c_str());
2824 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2828 //********************************************************************************************************************************
2829 void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
2835 GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
2837 DeleteMovie(path, bKeepId, idMovie);
2840 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
2844 if (NULL == m_pDB.get()) return ;
2845 if (NULL == m_pDS.get()) return ;
2848 idMovie = GetMovieId(strFilenameAndPath);
2856 strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2857 m_pDS->exec(strSQL.c_str());
2859 strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2860 m_pDS->exec(strSQL.c_str());
2862 strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2863 m_pDS->exec(strSQL.c_str());
2865 strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2866 m_pDS->exec(strSQL.c_str());
2868 strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2869 m_pDS->exec(strSQL.c_str());
2871 strSQL=PrepareSQL("delete from writerlinkmovie where idMovie=%i", idMovie);
2872 m_pDS->exec(strSQL.c_str());
2874 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2876 // keep the movie table entry, linking to tv shows, and bookmarks
2877 // so we can update the data in place
2878 // the ancilliary tables are still purged
2881 ClearBookMarksOfFile(strFilenameAndPath);
2883 strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2884 m_pDS->exec(strSQL.c_str());
2886 strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2887 m_pDS->exec(strSQL.c_str());
2889 CStdString strPath, strFileName;
2890 SplitPath(strFilenameAndPath,strPath,strFileName);
2891 InvalidatePathHash(strPath);
2894 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2896 AnnounceRemove("movie", idMovie);
2898 CommitTransaction();
2903 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2904 RollbackTransaction();
2908 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2914 GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2916 DeleteTvShow(path, bKeepId, idTvShow);
2919 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2923 if (NULL == m_pDB.get()) return ;
2924 if (NULL == m_pDS.get()) return ;
2927 idTvShow = GetTvShowId(strPath);
2934 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);
2935 m_pDS2->query(strSQL.c_str());
2936 while (!m_pDS2->eof())
2938 CStdString strFilenameAndPath;
2939 CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2940 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2941 ConstructPath(strFilenameAndPath, strPath, strFileName);
2942 DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2946 DeleteDetailsForTvShow(strPath, idTvShow);
2948 strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2949 m_pDS->exec(strSQL.c_str());
2951 // keep tvshow table and movielink table so we can update data in place
2954 strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2955 m_pDS->exec(strSQL.c_str());
2957 strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2958 m_pDS->exec(strSQL.c_str());
2960 strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2961 m_pDS->exec(strSQL.c_str());
2963 InvalidatePathHash(strPath);
2966 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2968 AnnounceRemove("tvshow", idTvShow);
2970 CommitTransaction();
2975 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2976 RollbackTransaction();
2980 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2986 GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2988 DeleteEpisode(path, idEpisode, bKeepId);
2991 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
2995 if (NULL == m_pDB.get()) return ;
2996 if (NULL == m_pDS.get()) return ;
2999 idEpisode = GetEpisodeId(strFilenameAndPath);
3006 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3008 AnnounceRemove("episode", idEpisode);
3011 strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
3012 m_pDS->exec(strSQL.c_str());
3014 strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
3015 m_pDS->exec(strSQL.c_str());
3017 strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
3018 m_pDS->exec(strSQL.c_str());
3020 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3022 // keep episode table entry and bookmarks so we can update the data in place
3023 // the ancilliary tables are still purged
3026 ClearBookMarksOfFile(strFilenameAndPath);
3028 strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
3029 m_pDS->exec(strSQL.c_str());
3035 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3039 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
3041 if (idMusicVideo < 0)
3045 GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
3047 DeleteMusicVideo(path, bKeepId, idMusicVideo);
3050 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
3054 if (NULL == m_pDB.get()) return ;
3055 if (NULL == m_pDS.get()) return ;
3058 idMVideo = GetMusicVideoId(strFilenameAndPath);
3066 strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
3067 m_pDS->exec(strSQL.c_str());
3069 strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
3070 m_pDS->exec(strSQL.c_str());
3072 strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
3073 m_pDS->exec(strSQL.c_str());
3075 strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
3076 m_pDS->exec(strSQL.c_str());
3078 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3080 // keep the music video table entry and bookmarks so we can update data in place
3081 // the ancilliary tables are still purged
3084 ClearBookMarksOfFile(strFilenameAndPath);
3086 strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
3087 m_pDS->exec(strSQL.c_str());
3089 CStdString strPath, strFileName;
3090 SplitPath(strFilenameAndPath,strPath,strFileName);
3091 InvalidatePathHash(strPath);
3094 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3096 AnnounceRemove("musicvideo", idMVideo);
3098 CommitTransaction();
3103 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3104 RollbackTransaction();
3108 void CVideoDatabase::DeleteStreamDetails(int idFile)
3110 m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
3113 void CVideoDatabase::DeleteSet(int idSet)
3117 if (NULL == m_pDB.get()) return ;
3118 if (NULL == m_pDS.get()) return ;
3121 strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
3122 m_pDS->exec(strSQL.c_str());
3123 strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3124 m_pDS->exec(strSQL.c_str());
3128 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3132 void CVideoDatabase::ClearMovieSet(int idMovie)
3134 SetMovieSet(idMovie, -1);
3137 void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
3140 ExecuteQuery(PrepareSQL("update movie set idSet = %i where idMovie = %i", idSet, idMovie));
3142 ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
3145 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3149 if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3153 if (mediaType == VIDEODB_CONTENT_MOVIES)
3155 else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3157 else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3158 type = "musicvideo";
3163 strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3164 m_pDS->exec(strSQL.c_str());
3168 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3172 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3174 GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3177 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3179 for (int i = min + 1; i < max; i++)
3181 switch (offsets[i].type)
3183 case VIDEODB_TYPE_STRING:
3184 *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3186 case VIDEODB_TYPE_INT:
3187 case VIDEODB_TYPE_COUNT:
3188 *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3190 case VIDEODB_TYPE_BOOL:
3191 *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3193 case VIDEODB_TYPE_FLOAT:
3194 *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3196 case VIDEODB_TYPE_STRINGARRAY:
3197 *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3199 case VIDEODB_TYPE_DATE:
3200 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3202 case VIDEODB_TYPE_DATETIME:
3203 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3209 DWORD movieTime = 0;
3212 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3214 CVideoInfoTag details;
3219 case VIDEODB_CONTENT_MOVIES:
3220 GetMovieInfo("", details, id);
3222 case VIDEODB_CONTENT_TVSHOWS:
3223 GetTvShowInfo("", details, id);
3225 case VIDEODB_CONTENT_EPISODES:
3226 GetEpisodeInfo("", details, id);
3228 case VIDEODB_CONTENT_MUSICVIDEOS:
3229 GetMusicVideoInfo("", details, id);
3238 bool CVideoDatabase::GetStreamDetails(CFileItem& item)
3240 // Note that this function (possibly) creates VideoInfoTags for items that don't have one yet!
3243 if (item.HasVideoInfoTag())
3244 fileId = item.GetVideoInfoTag()->m_iFileId;
3247 fileId = GetFileId(item);
3252 // Have a file id, get stream details if available (creates tag either way)
3253 item.GetVideoInfoTag()->m_iFileId = fileId;
3254 return GetStreamDetails(*item.GetVideoInfoTag());
3257 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3259 if (tag.m_iFileId < 0)
3262 bool retVal = false;
3264 CStreamDetails& details = tag.m_streamDetails;
3267 auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3270 CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3275 CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3278 case CStreamDetail::VIDEO:
3280 CStreamDetailVideo *p = new CStreamDetailVideo();
3281 p->m_strCodec = pDS->fv(2).get_asString();
3282 p->m_fAspect = pDS->fv(3).get_asFloat();
3283 p->m_iWidth = pDS->fv(4).get_asInt();
3284 p->m_iHeight = pDS->fv(5).get_asInt();
3285 p->m_iDuration = pDS->fv(10).get_asInt();
3286 p->m_strStereoMode = pDS->fv(11).get_asString();
3287 details.AddStream(p);
3291 case CStreamDetail::AUDIO:
3293 CStreamDetailAudio *p = new CStreamDetailAudio();
3294 p->m_strCodec = pDS->fv(6).get_asString();
3295 if (pDS->fv(7).get_isNull())
3296 p->m_iChannels = -1;
3298 p->m_iChannels = pDS->fv(7).get_asInt();
3299 p->m_strLanguage = pDS->fv(8).get_asString();
3300 details.AddStream(p);
3304 case CStreamDetail::SUBTITLE:
3306 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3307 p->m_strLanguage = pDS->fv(9).get_asString();
3308 details.AddStream(p);
3321 CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3323 details.DetermineBestStreams();
3325 if (details.GetVideoDuration() > 0)
3326 tag.m_duration = details.GetVideoDuration();
3331 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3333 if (tag.m_iFileId < 0)
3340 if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3342 CStackDirectory dir;
3343 CFileItemList fileList;
3344 dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3345 tag.m_resumePoint.Reset();
3346 for (int i = fileList.Size() - 1; i >= 0; i--)
3349 if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3351 tag.m_resumePoint = bookmark;
3352 tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3360 CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3361 m_pDS2->query( strSQL.c_str() );
3364 tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3365 tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3366 tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3367 tag.m_resumePoint.type = CBookmark::RESUME;
3375 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3381 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3383 return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3386 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3388 CVideoInfoTag details;
3393 DWORD time = XbmcThreads::SystemClockMillis();
3394 int idMovie = record->at(0).get_asInt();
3396 GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3398 details.m_iDbId = idMovie;
3399 details.m_type = "movie";
3401 details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3402 details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3403 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3404 details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3405 CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3406 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3407 details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3408 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3409 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3410 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3411 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3412 details.m_resumePoint.type = CBookmark::RESUME;
3414 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3418 GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3420 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3421 details.m_strPictureURL.Parse();
3424 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);
3425 m_pDS2->query(strSQL.c_str());
3426 while (!m_pDS2->eof())
3428 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3432 // create tvshowlink string
3434 GetLinksToTvShow(idMovie,links);
3435 for (unsigned int i=0;i<links.size();++i)
3437 CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3438 VIDEODB_ID_TV_TITLE,links[i]);
3439 m_pDS2->query(strSQL.c_str());
3441 details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3445 // get streamdetails
3446 GetStreamDetails(details);
3451 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3453 return GetDetailsForTvShow(pDS->get_sql_record(), getDetails, item);
3456 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3458 CVideoInfoTag details;
3463 DWORD time = XbmcThreads::SystemClockMillis();
3464 int idTvShow = record->at(0).get_asInt();
3466 GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3467 details.m_iDbId = idTvShow;
3468 details.m_type = "tvshow";
3469 details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3470 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3471 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3472 details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3473 details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3474 details.m_strShowPath = details.m_strPath;
3475 details.m_strShowTitle = details.m_strTitle;
3476 if (details.m_premiered.IsValid())
3477 details.m_iYear = details.m_premiered.GetYear();
3479 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3483 GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3486 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);
3487 m_pDS2->query(strSQL.c_str());
3488 while (!m_pDS2->eof())
3490 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3494 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3495 details.m_strPictureURL.Parse();
3500 item->m_dateTime = details.m_premiered;
3501 item->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
3502 item->SetProperty("totalepisodes", details.m_iEpisode);
3503 item->SetProperty("numepisodes", details.m_iEpisode); // will be changed later to reflect watchmode setting
3504 item->SetProperty("watchedepisodes", details.m_playCount);
3505 item->SetProperty("unwatchedepisodes", details.m_iEpisode - details.m_playCount);
3507 details.m_playCount = (details.m_iEpisode <= details.m_playCount) ? 1 : 0;
3512 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3514 return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3517 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3519 CVideoInfoTag details;
3524 DWORD time = XbmcThreads::SystemClockMillis();
3525 int idEpisode = record->at(0).get_asInt();
3527 GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3528 details.m_iDbId = idEpisode;
3529 details.m_type = "episode";
3530 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3531 details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3532 CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3533 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3534 details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3535 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3536 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3537 details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3538 details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3539 details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3540 details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3541 details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3542 details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3543 details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3545 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3546 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3547 details.m_resumePoint.type = CBookmark::RESUME;
3549 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3553 GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3554 GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3556 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3557 details.m_strPictureURL.Parse();
3558 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);
3559 m_pDS2->query(strSQL.c_str());
3561 details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3564 // get streamdetails
3565 GetStreamDetails(details);
3570 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3572 return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3575 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3577 CVideoInfoTag details;
3579 unsigned int time = XbmcThreads::SystemClockMillis();
3580 int idMVideo = record->at(0).get_asInt();
3582 GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3583 details.m_iDbId = idMVideo;
3584 details.m_type = "musicvideo";
3586 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3587 details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3588 CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3589 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3590 details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3591 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3592 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3593 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3594 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3595 details.m_resumePoint.type = CBookmark::RESUME;
3597 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3602 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);
3603 m_pDS2->query(strSQL.c_str());
3604 while (!m_pDS2->eof())
3606 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3611 details.m_strPictureURL.Parse();
3613 // get streamdetails
3614 GetStreamDetails(details);
3619 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3623 if (!m_pDB.get()) return;
3624 if (!m_pDS2.get()) return;
3626 CStdString sql = PrepareSQL("SELECT actors.strActor,"
3627 " actorlink%s.strRole,"
3628 " actorlink%s.iOrder,"
3633 " actorlink%s.idActor=actors.idActor"
3635 " art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3636 "WHERE actorlink%s.%s=%i "
3637 "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());
3638 m_pDS2->query(sql.c_str());
3639 while (!m_pDS2->eof())
3642 info.strName = m_pDS2->fv(0).get_asString();
3644 for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3646 if (i->strName == info.strName)
3654 info.strRole = m_pDS2->fv(1).get_asString();
3655 info.order = m_pDS2->fv(2).get_asInt();
3656 info.thumbUrl.ParseString(m_pDS2->fv(3).get_asString());
3657 info.thumb = m_pDS2->fv(4).get_asString();
3658 cast.push_back(info);
3666 CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3670 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3671 /// \retval Returns true if the settings exist, false otherwise.
3672 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3676 // obtain the FileID (if it exists)
3677 #ifdef NEW_VIDEODB_METHODS
3678 if (NULL == m_pDB.get()) return false;
3679 if (NULL == m_pDS.get()) return false;
3680 CStdString strPath, strFileName;
3681 URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3682 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());
3684 int idFile = GetFileId(strFilenameAndPath);
3685 if (idFile < 0) return false;
3686 if (NULL == m_pDB.get()) return false;
3687 if (NULL == m_pDS.get()) return false;
3688 // ok, now obtain the settings for this file
3689 CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3691 m_pDS->query( strSQL.c_str() );
3692 if (m_pDS->num_rows() > 0)
3693 { // get the video settings info
3694 settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3695 settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3696 settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3697 settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3698 settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3699 settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3700 settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3701 settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3702 settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3703 settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3704 settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3705 settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3706 settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3707 settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3708 settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3709 settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3710 settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3711 settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3712 settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3713 settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3714 settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3715 settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3716 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3717 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3718 settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3719 settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3720 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3721 settings.m_StereoMode = m_pDS->fv("StereoMode").get_asInt();
3722 settings.m_StereoInvert = m_pDS->fv("StereoInvert").get_asBool();
3723 settings.m_SubtitleCached = false;
3731 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3736 /// \brief Sets the settings for a particular video file
3737 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3741 if (NULL == m_pDB.get()) return ;
3742 if (NULL == m_pDS.get()) return ;
3743 int idFile = AddFile(strFilenameAndPath);
3746 CStdString strSQL = StringUtils::Format("select * from settings where idFile=%i", idFile);
3747 m_pDS->query( strSQL.c_str() );
3748 if (m_pDS->num_rows() > 0)
3752 strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3753 "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3754 "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3755 "DeinterlaceMode=%i,",
3756 setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3757 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3758 setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3759 setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3760 setting.m_DeinterlaceMode);
3762 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);
3764 m_pDS->exec(strSQL.c_str());
3770 strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3771 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3772 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3773 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3774 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode,StereoMode,StereoInvert) "
3776 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)",
3777 idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3778 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3779 setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3780 setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3781 setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3782 setting.m_DeinterlaceMode, setting.m_StereoMode, setting.m_StereoInvert);
3783 m_pDS->exec(strSQL.c_str());
3788 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3792 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3794 for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3795 SetArtForItem(mediaId, mediaType, i->first, i->second);
3798 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3802 if (NULL == m_pDB.get()) return;
3803 if (NULL == m_pDS.get()) return;
3805 // don't set <foo>.<bar> art types - these are derivative types from parent items
3806 if (artType.find('.') != string::npos)
3809 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());
3810 m_pDS->query(sql.c_str());
3813 int artId = m_pDS->fv(0).get_asInt();
3815 sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3816 m_pDS->exec(sql.c_str());
3821 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());
3822 m_pDS->exec(sql.c_str());
3827 CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3831 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3835 if (NULL == m_pDB.get()) return false;
3836 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3838 CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3839 m_pDS2->query(sql.c_str());
3840 while (!m_pDS2->eof())
3842 art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3846 return !art.empty();
3850 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3855 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3857 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());
3858 return GetSingleValue(query, m_pDS2);
3861 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::string &artType)
3863 return ExecuteQuery(PrepareSQL("DELETE FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str()));
3866 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::set<std::string> &artTypes)
3869 for (set<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
3870 result &= RemoveArtForItem(mediaId, mediaType, *i);
3875 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3879 if (NULL == m_pDB.get()) return false;
3880 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3882 // get all seasons for this show
3883 CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3884 m_pDS2->query(sql.c_str());
3886 vector< pair<int, int> > seasons;
3887 while (!m_pDS2->eof())
3889 seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3894 for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3896 map<string, string> art;
3897 GetArtForItem(i->first, "season", art);
3898 seasonArt.insert(make_pair(i->second,art));
3904 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3909 bool CVideoDatabase::GetArtTypes(const std::string &mediaType, std::vector<std::string> &artTypes)
3913 if (NULL == m_pDB.get()) return false;
3914 if (NULL == m_pDS.get()) return false;
3916 CStdString sql = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str());
3917 int numRows = RunQuery(sql);
3919 return numRows == 0;
3921 while (!m_pDS->eof())
3923 artTypes.push_back(m_pDS->fv(0).get_asString());
3931 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str());
3936 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3937 /// \retval Returns true if the stack times exist, false otherwise.
3938 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> ×)
3942 // obtain the FileID (if it exists)
3943 int idFile = GetFileId(filePath);
3944 if (idFile < 0) return false;
3945 if (NULL == m_pDB.get()) return false;
3946 if (NULL == m_pDS.get()) return false;
3947 // ok, now obtain the settings for this file
3948 CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3949 m_pDS->query( strSQL.c_str() );
3950 if (m_pDS->num_rows() > 0)
3951 { // get the video settings info
3952 CStdStringArray timeString;
3954 StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3956 for (unsigned int i = 0; i < timeString.size(); i++)
3958 times.push_back(atoi(timeString[i].c_str()));
3959 timeTotal += atoi(timeString[i].c_str());
3962 return (timeTotal > 0);
3968 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3973 /// \brief Sets the stack times for a particular video file
3974 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> ×)
3978 if (NULL == m_pDB.get()) return ;
3979 if (NULL == m_pDS.get()) return ;
3980 int idFile = AddFile(filePath);
3984 // delete any existing items
3985 m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3988 CStdString timeString = StringUtils::Format("%i", times[0]);
3989 for (unsigned int i = 1; i < times.size(); i++)
3990 timeString += StringUtils::Format(",%i", times[i]);
3992 m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3996 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4000 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
4002 if(URIUtils::IsMultiPath(strPath))
4004 vector<CStdString> paths;
4005 CMultiPathDirectory::GetPaths(strPath, paths);
4007 for(unsigned i=0;i<paths.size();i++)
4008 RemoveContentForPath(paths[i], progress);
4013 if (NULL == m_pDB.get()) return ;
4014 if (NULL == m_pDS.get()) return ;
4018 progress->SetHeading(700);
4019 progress->SetLine(0, "");
4020 progress->SetLine(1, 313);
4021 progress->SetLine(2, 330);
4022 progress->SetPercentage(0);
4023 progress->StartModal();
4024 progress->ShowProgressBar(true);
4026 vector< pair<int,string> > paths;
4027 GetSubPaths(strPath, paths);
4029 for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
4031 bool bMvidsChecked=false;
4034 progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
4035 progress->Progress();
4038 if (HasTvShowInfo(i->second))
4039 DeleteTvShow(i->second);
4042 CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
4043 m_pDS2->query(strSQL.c_str());
4046 strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4047 m_pDS2->query(strSQL.c_str());
4048 bMvidsChecked = true;
4050 while (!m_pDS2->eof())
4052 CStdString strMoviePath;
4053 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
4054 ConstructPath(strMoviePath, i->second, strFileName);
4055 if (HasMovieInfo(strMoviePath))
4056 DeleteMovie(strMoviePath);
4057 if (HasMusicVideoInfo(strMoviePath))
4058 DeleteMusicVideo(strMoviePath);
4060 if (m_pDS2->eof() && !bMvidsChecked)
4062 strSQL =PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4063 m_pDS2->query(strSQL.c_str());
4064 bMvidsChecked = true;
4068 m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
4074 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
4080 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
4082 // if we have a multipath, set scraper for all contained paths too
4083 if(URIUtils::IsMultiPath(filePath))
4085 vector<CStdString> paths;
4086 CMultiPathDirectory::GetPaths(filePath, paths);
4088 for(unsigned i=0;i<paths.size();i++)
4089 SetScraperForPath(paths[i],scraper,settings);
4094 if (NULL == m_pDB.get()) return ;
4095 if (NULL == m_pDS.get()) return ;
4097 int idPath = AddPath(filePath);
4103 if (settings.exclude)
4104 { //NB See note in ::GetScraperForPath about strContent=='none'
4105 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
4108 { // catch clearing content, but not excluding
4109 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
4113 CStdString content = TranslateContent(scraper->Content());
4114 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);
4116 m_pDS->exec(strSQL.c_str());
4120 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4124 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
4128 if (NULL == m_pDB.get()) return false;
4129 if (NULL == m_pDS.get()) return false;
4131 CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
4132 if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
4134 bool found = m_pDS->fv(0).get_asInt() > 0;
4140 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
4148 CArtItem() { art_id = 0; media_id = 0; };
4156 void CVideoDatabase::UpdateTables(int iVersion)
4160 m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4161 m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4164 { // add seasons table
4165 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4166 // insert all seasons for each show
4167 m_pDS->query("SELECT idShow FROM tvshow");
4168 while (!m_pDS->eof())
4170 CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4175 " JOIN tvshowlinkepisode ON"
4176 " episode.idEpisode=tvshowlinkepisode.idEpisode"
4177 " WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4178 m_pDS2->exec(sql.c_str());
4179 // and the "all seasons node"
4180 sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4181 m_pDS2->exec(sql.c_str());
4187 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4189 CMediaSettings::Get().SetVideoNeedsUpdate(63);
4190 CSettings::Get().Save();
4193 { // add idShow to episode table
4194 m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4195 m_pDS->query("SELECT idEpisode FROM episode");
4196 while (!m_pDS->eof())
4198 int idEpisode = m_pDS->fv(0).get_asInt();
4199 CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4200 m_pDS2->exec(update.c_str());
4203 m_pDS->exec("DROP TABLE tvshowlinkepisode");
4207 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4208 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4211 { // add idSet to movie table
4212 m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4213 m_pDS->query("SELECT idMovie FROM movie");
4214 while (!m_pDS->eof())
4216 int idMovie = m_pDS->fv(0).get_asInt();
4217 CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4218 m_pDS2->exec(sql.c_str());
4221 m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4224 { // update old art URLs
4225 m_pDS->query("select art_id,url from art where url like 'image://%%'");
4226 vector< pair<int, string> > art;
4227 while (!m_pDS->eof())
4229 art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4233 for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4234 m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4237 { // update URL encoded paths
4238 m_pDS->query("select idFile, strFilename from files");
4239 vector< pair<int, string> > files;
4240 while (!m_pDS->eof())
4242 files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4247 for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4249 std::string filename = i->second;
4250 bool update = URIUtils::UpdateUrlEncoding(filename) &&
4251 (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4255 m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4259 { // Update thumb to poster or banner as applicable
4260 CTextureDatabase db;
4263 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')");
4264 vector<CArtItem> art;
4265 while (!m_pDS->eof())
4267 CTextureDetails details;
4268 if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4271 item.art_id = m_pDS->fv(0).get_asInt();
4272 item.art_url = m_pDS->fv(1).get_asString();
4273 item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4274 item.media_id = m_pDS->fv(2).get_asInt();
4275 item.media_type = m_pDS->fv(3).get_asString();
4276 if (item.art_type != "thumb")
4277 art.push_back(item);
4282 for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4284 if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4285 m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4287 m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4292 { // update the runtime columns
4293 vector< pair<string, int> > tables;
4294 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4295 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4296 tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4297 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4299 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);
4300 m_pDS->query(sql.c_str());
4301 vector< pair<int, int> > videos;
4302 while (!m_pDS->eof())
4304 int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4306 videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4310 for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4311 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));
4316 m_pDS->exec("ALTER TABLE settings ADD StereoMode integer");
4317 m_pDS->exec("ALTER TABLE settings ADD StereoInvert bool");
4320 m_pDS->exec("ALTER TABLE streamdetails ADD strStereoMode text");
4323 int CVideoDatabase::GetSchemaVersion() const
4328 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4330 SScanSettings settings;
4331 bool foundDirectly = false;
4332 ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4333 if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4334 return false; // episodes
4335 return settings.parent_name_root; // shows, movies, musicvids
4338 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4340 if(URIUtils::IsMultiPath(strPath))
4342 vector<CStdString> paths;
4343 CMultiPathDirectory::GetPaths(strPath, paths);
4346 for(unsigned i=0;i<paths.size();i++)
4347 ret |= GetPlayCounts(paths[i], items);
4352 if (URIUtils::IsPlugin(strPath))
4355 pathID = GetPathId(url.GetWithoutFilename());
4358 pathID = GetPathId(strPath);
4360 return false; // path (and thus files) aren't in the database
4365 if (NULL == m_pDB.get()) return false;
4366 if (NULL == m_pDS.get()) return false;
4368 // TODO: also test a single query for the above and below
4369 CStdString sql = PrepareSQL(
4371 " files.strFilename, files.playCount,"
4372 " bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4374 " LEFT JOIN bookmark ON"
4375 " files.idFile = bookmark.idFile AND bookmark.type = %i"
4376 " WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4378 if (RunQuery(sql) <= 0)
4381 items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4382 while (!m_pDS->eof())
4385 ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4386 CFileItemPtr item = items.Get(path);
4389 item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4390 if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4392 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4393 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4394 item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4403 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4408 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4410 int id = GetFileId(item);
4412 return 0; // not in db, so not watched
4417 if (NULL == m_pDB.get()) return -1;
4418 if (NULL == m_pDS.get()) return -1;
4420 CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4422 if (m_pDS->query(strSQL.c_str()))
4424 // there should only ever be one row returned
4425 if (m_pDS->num_rows() == 1)
4426 count = m_pDS->fv(0).get_asInt();
4433 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4438 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4440 if (NULL == m_pDB.get()) return;
4441 if (NULL == m_pDS.get()) return;
4442 if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4445 if (type == VIDEODB_CONTENT_TVSHOWS)
4446 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);
4447 else if (type == VIDEODB_CONTENT_MOVIES)
4448 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);
4452 m_pDS->exec(exec.c_str());
4454 if (type == VIDEODB_CONTENT_TVSHOWS)
4455 AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4456 else if (type == VIDEODB_CONTENT_MOVIES)
4457 AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4461 CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4465 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4468 if (item.HasProperty("original_listitem_url") &&
4469 URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4471 CFileItem item2(item);
4472 item2.SetPath(item.GetProperty("original_listitem_url").asString());
4473 id = AddFile(item2);
4480 // and mark as watched
4483 if (NULL == m_pDB.get()) return ;
4484 if (NULL == m_pDS.get()) return ;
4489 if (!date.IsValid())
4490 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4492 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4496 if (!date.IsValid())
4497 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4499 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4502 m_pDS->exec(strSQL.c_str());
4504 // We only need to announce changes to video items in the library
4505 if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4507 // Only provide the "playcount" value if it has actually changed
4508 if (item.GetVideoInfoTag()->m_playCount != count)
4511 data["playcount"] = count;
4512 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4515 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4520 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4524 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4526 SetPlayCount(item, GetPlayCount(item) + 1);
4529 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4531 SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4534 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4538 if (NULL == m_pDB.get()) return ;
4539 if (NULL == m_pDS.get()) return ;
4541 if (iType == VIDEODB_CONTENT_MOVIES)
4543 CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4546 else if (iType == VIDEODB_CONTENT_EPISODES)
4548 CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4549 content = "episode";
4551 else if (iType == VIDEODB_CONTENT_TVSHOWS)
4553 CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4556 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4558 CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4559 content = "musicvideo";
4561 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4563 CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4564 CStdString strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4565 m_pDS->exec(strSQL.c_str());
4568 if (!content.empty())
4570 SetSingleValue(iType, idMovie, FieldTitle, strNewMovieTitle);
4571 AnnounceUpdate(content, idMovie);
4576 CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4580 bool CVideoDatabase::UpdateVideoSortTitle(int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType /* = VIDEODB_CONTENT_MOVIES */)
4584 if (NULL == m_pDB.get() || NULL == m_pDS.get())
4586 if (iType != VIDEODB_CONTENT_MOVIES && iType != VIDEODB_CONTENT_TVSHOWS)
4589 CStdString content = "movie";
4590 if (iType == VIDEODB_CONTENT_TVSHOWS)
4593 if (SetSingleValue(iType, idDb, FieldSortTitle, strNewSortTitle))
4595 AnnounceUpdate(content, idDb);
4601 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());
4607 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4608 void CVideoDatabase::EraseVideoSettings()
4612 CLog::Log(LOGINFO, "Deleting settings information for all movies");
4613 m_pDS->exec("delete from settings");
4617 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4621 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4623 return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4626 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4628 return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4631 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4633 return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4636 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4640 if (NULL == m_pDB.get()) return false;
4641 if (NULL == m_pDS.get()) return false;
4644 Filter extFilter = filter;
4645 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4647 if (idContent == VIDEODB_CONTENT_MOVIES)
4649 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4650 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());
4651 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",
4652 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4654 else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4656 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4657 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4658 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",
4659 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4661 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4663 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4664 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());
4665 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",
4666 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4673 if (idContent == VIDEODB_CONTENT_MOVIES)
4675 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4676 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());
4677 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",
4678 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4679 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4681 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4683 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4684 extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4685 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4686 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4688 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4690 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4691 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());
4692 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",
4693 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4694 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4702 extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4703 extFilter.group.clear();
4704 extFilter.order.clear();
4706 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4708 CVideoDbUrl videoUrl;
4709 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4712 int iRowsFound = RunQuery(strSQL);
4713 if (iRowsFound <= 0)
4714 return iRowsFound == 0;
4718 CFileItemPtr pItem(new CFileItem());
4719 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4726 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4728 map<int, pair<CStdString,int> > mapItems;
4729 map<int, pair<CStdString,int> >::iterator it;
4730 while (!m_pDS->eof())
4732 int id = m_pDS->fv(0).get_asInt();
4733 CStdString str = m_pDS->fv(1).get_asString();
4735 // was this already found?
4736 it = mapItems.find(id);
4737 if (it == mapItems.end())
4740 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
4742 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4743 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
4744 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4745 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
4752 for (it = mapItems.begin(); it != mapItems.end(); ++it)
4754 CFileItemPtr pItem(new CFileItem(it->second.first));
4755 pItem->GetVideoInfoTag()->m_iDbId = it->first;
4756 pItem->GetVideoInfoTag()->m_type = type;
4758 CVideoDbUrl itemUrl = videoUrl;
4759 CStdString path = StringUtils::Format("%ld/", it->first);
4760 itemUrl.AppendPath(path);
4761 pItem->SetPath(itemUrl.ToString());
4763 pItem->m_bIsFolder = true;
4764 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4765 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
4766 if (!items.Contains(pItem->GetPath()))
4768 pItem->SetLabelPreformated(true);
4775 while (!m_pDS->eof())
4777 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4778 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
4779 pItem->GetVideoInfoTag()->m_type = type;
4781 CVideoDbUrl itemUrl = videoUrl;
4782 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
4783 itemUrl.AppendPath(path);
4784 pItem->SetPath(itemUrl.ToString());
4786 pItem->m_bIsFolder = true;
4787 pItem->SetLabelPreformated(true);
4788 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4789 { // fv(3) is the number of videos watched, fv(2) is the total number. We set the playcount
4790 // only if the number of videos watched is equal to the total number (i.e. every video watched)
4791 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
4802 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4807 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4809 CStdString mediaType;
4810 if (idContent == VIDEODB_CONTENT_MOVIES)
4811 mediaType = "movie";
4812 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4813 mediaType = "tvshow";
4814 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4815 mediaType = "musicvideo";
4821 if (NULL == m_pDB.get()) return false;
4822 if (NULL == m_pDS.get()) return false;
4824 CStdString strSQL = "SELECT %s FROM taglinks ";
4826 Filter extFilter = filter;
4827 extFilter.fields = "tag.idTag, tag.strTag";
4828 extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
4830 if (idContent == (int)VIDEODB_CONTENT_MOVIES)
4831 extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
4833 extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
4834 extFilter.AppendGroup("taglinks.idTag");
4838 extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
4839 extFilter.group.clear();
4840 extFilter.order.clear();
4842 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4844 // parse the base path to get additional filters
4845 CVideoDbUrl videoUrl;
4846 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4849 int iRowsFound = RunQuery(strSQL);
4850 if (iRowsFound <= 0)
4851 return iRowsFound == 0;
4855 CFileItemPtr pItem(new CFileItem());
4856 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4863 while (!m_pDS->eof())
4865 int idTag = m_pDS->fv(0).get_asInt();
4867 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4868 pItem->m_bIsFolder = true;
4869 pItem->GetVideoInfoTag()->m_iDbId = idTag;
4870 pItem->GetVideoInfoTag()->m_type = "tag";
4872 CVideoDbUrl itemUrl = videoUrl;
4873 CStdString path = StringUtils::Format("%ld/", idTag);
4874 itemUrl.AppendPath(path);
4875 pItem->SetPath(itemUrl.ToString());
4877 if (!items.Contains(pItem->GetPath()))
4879 pItem->SetLabelPreformated(true);
4891 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4896 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool ignoreSingleMovieSets /* = false */)
4898 if (idContent != VIDEODB_CONTENT_MOVIES)
4901 return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
4904 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
4908 if (NULL == m_pDB.get()) return false;
4909 if (NULL == m_pDS.get()) return false;
4911 CVideoDbUrl videoUrl;
4912 if (!videoUrl.FromString(strBaseDir))
4915 Filter setFilter = filter;
4916 setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
4917 if (!setFilter.order.empty())
4918 setFilter.order += ",";
4919 setFilter.order += "sets.idSet";
4921 if (!GetMoviesByWhere(strBaseDir, setFilter, items))
4925 if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
4935 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4940 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4944 if (NULL == m_pDB.get()) return false;
4945 if (NULL == m_pDS.get()) return false;
4947 CVideoDbUrl videoUrl;
4948 if (!videoUrl.FromString(strBaseDir))
4951 CStdString strSQL = "select %s from musicvideoview ";
4952 Filter extFilter = filter;
4953 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
4954 extFilter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
4955 extFilter.AppendJoin(PrepareSQL("join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
4956 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4958 extFilter.fields += " path.strPath";
4959 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath");
4963 videoUrl.AddOption("artistid", idArtist);
4965 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
4969 extFilter.fields = "COUNT(1)";
4970 extFilter.group.clear();
4971 extFilter.order.clear();
4973 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4975 if (!BuildSQL(videoUrl.ToString(), strSQL, extFilter, strSQL, videoUrl))
4978 int iRowsFound = RunQuery(strSQL);
4979 if (iRowsFound <= 0)
4980 return iRowsFound == 0;
4984 CFileItemPtr pItem(new CFileItem());
4985 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4992 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4994 map<int, pair<CStdString,CStdString> > mapAlbums;
4995 map<int, pair<CStdString,CStdString> >::iterator it;
4996 while (!m_pDS->eof())
4998 int lidMVideo = m_pDS->fv(1).get_asInt();
4999 CStdString strAlbum = m_pDS->fv(0).get_asString();
5000 it = mapAlbums.find(lidMVideo);
5001 // was this genre already found?
5002 if (it == mapAlbums.end())
5005 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5006 mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5012 for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5014 if (!it->second.first.empty())
5016 CFileItemPtr pItem(new CFileItem(it->second.first));
5018 CVideoDbUrl itemUrl = videoUrl;
5019 CStdString path = StringUtils::Format("%ld/", it->first);
5020 itemUrl.AppendPath(path);
5021 pItem->SetPath(itemUrl.ToString());
5023 pItem->m_bIsFolder=true;
5024 pItem->SetLabelPreformated(true);
5025 if (!items.Contains(pItem->GetPath()))
5027 pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5035 while (!m_pDS->eof())
5037 if (!m_pDS->fv(0).get_asString().empty())
5039 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5041 CVideoDbUrl itemUrl = videoUrl;
5042 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(1).get_asInt());
5043 itemUrl.AppendPath(path);
5044 pItem->SetPath(itemUrl.ToString());
5046 pItem->m_bIsFolder=true;
5047 pItem->SetLabelPreformated(true);
5048 if (!items.Contains(pItem->GetPath()))
5050 pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5059 // CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5064 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5069 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5071 return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5074 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5076 return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5079 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5081 if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5082 { // set thumbs - ideally this should be in the normal thumb setting routines
5083 for (int i = 0; i < items.Size() && !countOnly; i++)
5085 CFileItemPtr pItem = items[i];
5086 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5087 pItem->SetIconImage("DefaultArtist.png");
5089 pItem->SetIconImage("DefaultActor.png");
5096 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5098 if (NULL == m_pDB.get()) return false;
5099 if (NULL == m_pDS.get()) return false;
5103 // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5104 // point. This means that we *MUST* filter these levels as you'll get double ups. Ideally we'd allow playcount
5105 // to filter through as we normally do for tvshows to save this happening.
5106 // Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5107 // Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5110 // General routine that the other actor/director/writer routines call
5112 // get primary genres for movies
5114 Filter extFilter = filter;
5115 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5117 if (idContent == VIDEODB_CONTENT_MOVIES)
5119 strSQL = "select %s from actors ";
5120 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5121 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",
5122 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5124 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5126 strSQL = "select %s from actors ";
5127 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5128 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",
5129 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5131 else if (idContent == VIDEODB_CONTENT_EPISODES)
5133 strSQL = "select %s from actors ";
5134 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5135 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",
5136 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5138 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5140 strSQL = "select %s from actors ";
5141 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5142 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",
5143 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5150 if (idContent == VIDEODB_CONTENT_MOVIES)
5152 strSQL ="select %s from actors ";
5153 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5154 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",
5155 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5156 extFilter.AppendGroup("actors.idActor");
5158 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5160 strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5161 extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5162 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5163 type.c_str(), type.c_str(), type.c_str()));
5165 else if (idContent == VIDEODB_CONTENT_EPISODES)
5167 strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5168 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5169 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5170 type.c_str(), type.c_str(), type.c_str()));
5171 extFilter.AppendGroup("actors.idActor");
5173 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5175 strSQL = "select %s from actors ";
5176 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5177 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",
5178 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5179 extFilter.AppendGroup("actors.idActor");
5187 extFilter.fields = "COUNT(1)";
5188 extFilter.group.clear();
5189 extFilter.order.clear();
5191 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5193 CVideoDbUrl videoUrl;
5194 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5198 unsigned int time = XbmcThreads::SystemClockMillis();
5199 if (!m_pDS->query(strSQL.c_str())) return false;
5200 CLog::Log(LOGDEBUG, "%s - query took %i ms",
5201 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5202 int iRowsFound = m_pDS->num_rows();
5203 if (iRowsFound == 0)
5211 CFileItemPtr pItem(new CFileItem());
5212 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5219 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5221 map<int, CActor> mapActors;
5222 map<int, CActor>::iterator it;
5224 while (!m_pDS->eof())
5226 int idActor = m_pDS->fv(0).get_asInt();
5228 actor.name = m_pDS->fv(1).get_asString();
5229 actor.thumb = m_pDS->fv(2).get_asString();
5230 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5231 actor.playcount = m_pDS->fv(3).get_asInt();
5232 it = mapActors.find(idActor);
5233 // is this actor already known?
5234 if (it == mapActors.end())
5237 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5238 mapActors.insert(pair<int, CActor>(idActor, actor));
5244 for (it=mapActors.begin();it != mapActors.end();++it)
5246 CFileItemPtr pItem(new CFileItem(it->second.name));
5248 CVideoDbUrl itemUrl = videoUrl;
5249 CStdString path = StringUtils::Format("%ld/", it->first);
5250 itemUrl.AppendPath(path);
5251 pItem->SetPath(itemUrl.ToString());
5253 pItem->m_bIsFolder=true;
5254 pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5255 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5256 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5257 pItem->GetVideoInfoTag()->m_type = type;
5263 while (!m_pDS->eof())
5267 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5269 CVideoDbUrl itemUrl = videoUrl;
5270 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5271 itemUrl.AppendPath(path);
5272 pItem->SetPath(itemUrl.ToString());
5274 pItem->m_bIsFolder=true;
5275 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5276 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5277 pItem->GetVideoInfoTag()->m_type = type;
5278 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5280 // fv(4) is the number of videos watched, fv(3) is the total number. We set the playcount
5281 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5282 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5284 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5285 pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5292 CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5293 return items.Size() > 0;
5298 CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5299 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5306 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5311 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5315 if (NULL == m_pDB.get()) return false;
5316 if (NULL == m_pDS.get()) return false;
5319 Filter extFilter = filter;
5320 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5322 if (idContent == VIDEODB_CONTENT_MOVIES)
5324 strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5325 extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5327 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5329 strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5330 extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5332 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5334 strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5335 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5343 if (idContent == VIDEODB_CONTENT_MOVIES)
5345 strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5346 extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5347 extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5349 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5350 strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5351 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5353 strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5354 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5355 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5361 CVideoDbUrl videoUrl;
5362 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5365 int iRowsFound = RunQuery(strSQL);
5366 if (iRowsFound <= 0)
5367 return iRowsFound == 0;
5369 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5371 map<int, pair<CStdString,int> > mapYears;
5372 map<int, pair<CStdString,int> >::iterator it;
5373 while (!m_pDS->eof())
5376 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5379 time.SetFromDateString(m_pDS->fv(0).get_asString());
5380 lYear = time.GetYear();
5382 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5383 lYear = m_pDS->fv(0).get_asInt();
5384 it = mapYears.find(lYear);
5385 if (it == mapYears.end())
5388 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5390 CStdString year = StringUtils::Format("%d", lYear);
5391 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5392 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5394 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5401 for (it=mapYears.begin();it != mapYears.end();++it)
5405 CFileItemPtr pItem(new CFileItem(it->second.first));
5407 CVideoDbUrl itemUrl = videoUrl;
5408 CStdString path = StringUtils::Format("%ld/", it->first);
5409 itemUrl.AppendPath(path);
5410 pItem->SetPath(itemUrl.ToString());
5412 pItem->m_bIsFolder=true;
5413 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5414 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5420 while (!m_pDS->eof())
5423 CStdString strLabel;
5424 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5427 time.SetFromDateString(m_pDS->fv(0).get_asString());
5428 lYear = time.GetYear();
5429 strLabel = StringUtils::Format("%i",lYear);
5431 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5433 lYear = m_pDS->fv(0).get_asInt();
5434 strLabel = m_pDS->fv(0).get_asString();
5441 CFileItemPtr pItem(new CFileItem(strLabel));
5443 CVideoDbUrl itemUrl = videoUrl;
5444 CStdString path = StringUtils::Format("%ld/", lYear);
5445 itemUrl.AppendPath(path);
5446 pItem->SetPath(itemUrl.ToString());
5448 pItem->m_bIsFolder=true;
5449 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5451 // fv(2) is the number of videos watched, fv(1) is the total number. We set the playcount
5452 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5453 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5456 // take care of dupes ..
5457 if (!items.Contains(pItem->GetPath()))
5469 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5474 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5478 if (NULL == m_pDB.get()) return false;
5479 if (NULL == m_pDS.get()) return false;
5481 // look for duplicate show titles and stack them into a list
5484 CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5485 CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5486 if (!m_pDS->query(strSQL.c_str())) return false;
5487 int iRows = m_pDS->num_rows();
5488 if (iRows == 0) return false; // this should never happen!
5490 { // more than one show, so stack them up
5492 while (!m_pDS->eof())
5494 strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5497 strIn[strIn.size() - 1] = ')'; // replace last , with )
5504 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5509 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5513 if (NULL == m_pDB.get()) return false;
5514 if (NULL == m_pDS.get()) return false;
5516 // parse the base path to get additional filters
5517 CVideoDbUrl videoUrl;
5518 if (!videoUrl.FromString(strBaseDir))
5521 CStdString strIn = PrepareSQL("= %i", idShow);
5522 GetStackedTvShowList(idShow, strIn);
5524 CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5526 "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5527 "seasons.idSeason, "
5528 "count(1), count(files.playCount) "
5529 "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);
5532 filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5533 "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5534 "JOIN files ON files.idFile = episodeview.idFile "
5535 "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5536 "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5537 filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5538 filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5540 videoUrl.AddOption("tvshowid", idShow);
5543 videoUrl.AddOption("actorid", idActor);
5544 else if (idDirector != -1)
5545 videoUrl.AddOption("directorid", idDirector);
5546 else if (idGenre != -1)
5547 videoUrl.AddOption("genreid", idGenre);
5548 else if (idYear != -1)
5549 videoUrl.AddOption("year", idYear);
5551 if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5554 int iRowsFound = RunQuery(strSQL);
5555 if (iRowsFound <= 0)
5556 return iRowsFound == 0;
5558 // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5559 CStdString showTitle = m_pDS->fv(2).get_asString();
5560 CStdString showPlot = m_pDS->fv(3).get_asString();
5561 CStdString showPremiered = m_pDS->fv(4).get_asString();
5562 CStdString showStudio = m_pDS->fv(6).get_asString();
5563 CStdString showMPAARating = m_pDS->fv(7).get_asString();
5565 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5567 map<int, CSeason> mapSeasons;
5568 map<int, CSeason>::iterator it;
5569 while (!m_pDS->eof())
5571 int iSeason = m_pDS->fv(0).get_asInt();
5572 it = mapSeasons.find(iSeason);
5574 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5579 if (it == mapSeasons.end())
5582 season.path = m_pDS->fv(1).get_asString();
5583 season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5584 season.id = m_pDS->fv(8).get_asInt();
5585 season.numEpisodes = m_pDS->fv(9).get_asInt();
5586 season.numWatched = m_pDS->fv(10).get_asInt();
5587 mapSeasons.insert(make_pair(iSeason, season));
5593 for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5595 int iSeason = it->first;
5596 CStdString strLabel;
5598 strLabel = g_localizeStrings.Get(20381);
5600 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5601 CFileItemPtr pItem(new CFileItem(strLabel));
5603 CVideoDbUrl itemUrl = videoUrl;
5604 CStdString strDir = StringUtils::Format("%ld/", it->first);
5605 itemUrl.AppendPath(strDir);
5606 pItem->SetPath(itemUrl.ToString());
5608 pItem->m_bIsFolder=true;
5609 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5610 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5611 pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5612 pItem->GetVideoInfoTag()->m_type = "season";
5613 pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5614 pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5615 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5616 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5617 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5618 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5619 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5620 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5621 pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5622 pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5623 pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5624 pItem->SetProperty("watchedepisodes", it->second.numWatched);
5625 pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5626 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5627 pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5628 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5634 while (!m_pDS->eof())
5636 int iSeason = m_pDS->fv(0).get_asInt();
5637 CStdString strLabel;
5639 strLabel = g_localizeStrings.Get(20381);
5641 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5642 CFileItemPtr pItem(new CFileItem(strLabel));
5644 CVideoDbUrl itemUrl = videoUrl;
5645 CStdString strDir = StringUtils::Format("%ld/", iSeason);
5646 itemUrl.AppendPath(strDir);
5647 pItem->SetPath(itemUrl.ToString());
5649 pItem->m_bIsFolder=true;
5650 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5651 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5652 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5653 pItem->GetVideoInfoTag()->m_type = "season";
5654 pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5655 pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5656 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5657 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5658 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5659 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5660 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5661 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5662 int totalEpisodes = m_pDS->fv(9).get_asInt();
5663 int watchedEpisodes = m_pDS->fv(10).get_asInt();
5664 pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5665 pItem->SetProperty("totalepisodes", totalEpisodes);
5666 pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5667 pItem->SetProperty("watchedepisodes", watchedEpisodes);
5668 pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5669 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5670 pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5671 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5678 // now add any linked movies
5679 if (getLinkedMovies)
5682 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5683 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5684 CFileItemList movieItems;
5685 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
5687 if (movieItems.Size() > 0)
5688 items.Append(movieItems);
5695 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5700 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5702 if (NULL == m_pDB.get() || NULL == m_pDS.get())
5705 if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5708 SortDescription sorting = sortDescription;
5709 if (sortDescription.sortBy == SortByFile ||
5710 sortDescription.sortBy == SortByTitle ||
5711 sortDescription.sortBy == SortBySortTitle ||
5712 sortDescription.sortBy == SortByLabel ||
5713 sortDescription.sortBy == SortByDateAdded ||
5714 sortDescription.sortBy == SortByRating ||
5715 sortDescription.sortBy == SortByYear ||
5716 sortDescription.sortBy == SortByLastPlayed ||
5717 sortDescription.sortBy == SortByPlaycount)
5718 sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5720 bool success = false;
5723 case MediaTypeMovie:
5724 success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5727 case MediaTypeTvShow:
5728 success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
5731 case MediaTypeEpisode:
5732 success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
5735 case MediaTypeMusicVideo:
5736 success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
5743 items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
5747 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5749 CVideoDbUrl videoUrl;
5750 if (!videoUrl.FromString(strBaseDir))
5753 return GetItems(strBaseDir, videoUrl.GetType(), videoUrl.GetItemType(), items, filter, sortDescription);
5756 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, const CStdString &mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5758 VIDEODB_CONTENT_TYPE contentType;
5759 if (mediaType.Equals("movies"))
5760 contentType = VIDEODB_CONTENT_MOVIES;
5761 else if (mediaType.Equals("tvshows"))
5763 if (itemType.Equals("episodes"))
5764 contentType = VIDEODB_CONTENT_EPISODES;
5766 contentType = VIDEODB_CONTENT_TVSHOWS;
5768 else if (mediaType.Equals("musicvideos"))
5769 contentType = VIDEODB_CONTENT_MUSICVIDEOS;
5773 return GetItems(strBaseDir, contentType, itemType, items, filter, sortDescription);
5776 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, VIDEODB_CONTENT_TYPE mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5778 if (itemType.Equals("movies") && (mediaType == VIDEODB_CONTENT_MOVIES || mediaType == VIDEODB_CONTENT_MOVIE_SETS))
5779 return GetMoviesByWhere(strBaseDir, filter, items, sortDescription);
5780 else if (itemType.Equals("tvshows") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5781 return GetTvShowsByWhere(strBaseDir, filter, items, sortDescription);
5782 else if (itemType.Equals("musicvideos") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5783 return GetMusicVideosByWhere(strBaseDir, filter, items, true, sortDescription);
5784 else if (itemType.Equals("episodes") && mediaType == VIDEODB_CONTENT_EPISODES)
5785 return GetEpisodesByWhere(strBaseDir, filter, items, true, sortDescription);
5786 else if (itemType.Equals("seasons") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5787 return GetSeasonsNav(strBaseDir, items);
5788 else if (itemType.Equals("genres"))
5789 return GetGenresNav(strBaseDir, items, mediaType, filter);
5790 else if (itemType.Equals("years"))
5791 return GetYearsNav(strBaseDir, items, mediaType, filter);
5792 else if (itemType.Equals("actors"))
5793 return GetActorsNav(strBaseDir, items, mediaType, filter);
5794 else if (itemType.Equals("directors"))
5795 return GetDirectorsNav(strBaseDir, items, mediaType, filter);
5796 else if (itemType.Equals("writers"))
5797 return GetWritersNav(strBaseDir, items, mediaType, filter);
5798 else if (itemType.Equals("studios"))
5799 return GetStudiosNav(strBaseDir, items, mediaType, filter);
5800 else if (itemType.Equals("sets"))
5801 return GetSetsNav(strBaseDir, items, mediaType, filter);
5802 else if (itemType.Equals("countries"))
5803 return GetCountriesNav(strBaseDir, items, mediaType, filter);
5804 else if (itemType.Equals("tags"))
5805 return GetTagsNav(strBaseDir, items, mediaType, filter);
5806 else if (itemType.Equals("artists") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5807 return GetActorsNav(strBaseDir, items, mediaType, filter);
5808 else if (itemType.Equals("albums") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5809 return GetMusicVideoAlbumsNav(strBaseDir, items, -1, filter);
5814 CStdString CVideoDatabase::GetItemById(const CStdString &itemType, int id)
5816 if (itemType.Equals("genres"))
5817 return GetGenreById(id);
5818 else if (itemType.Equals("years"))
5819 return StringUtils::Format("%d", id);
5820 else if (itemType.Equals("actors") || itemType.Equals("directors") || itemType.Equals("artists"))
5821 return GetPersonById(id);
5822 else if (itemType.Equals("studios"))
5823 return GetStudioById(id);
5824 else if (itemType.Equals("sets"))
5825 return GetSetById(id);
5826 else if (itemType.Equals("countries"))
5827 return GetCountryById(id);
5828 else if (itemType.Equals("tags"))
5829 return GetTagById(id);
5830 else if (itemType.Equals("albums"))
5831 return GetMusicVideoAlbumById(id);
5836 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
5837 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
5838 int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
5839 const SortDescription &sortDescription /* = SortDescription() */)
5841 CVideoDbUrl videoUrl;
5842 if (!videoUrl.FromString(strBaseDir))
5846 videoUrl.AddOption("genreid", idGenre);
5847 else if (idCountry > 0)
5848 videoUrl.AddOption("countryid", idCountry);
5849 else if (idStudio > 0)
5850 videoUrl.AddOption("studioid", idStudio);
5851 else if (idDirector > 0)
5852 videoUrl.AddOption("directorid", idDirector);
5853 else if (idYear > 0)
5854 videoUrl.AddOption("year", idYear);
5855 else if (idActor > 0)
5856 videoUrl.AddOption("actorid", idActor);
5858 videoUrl.AddOption("setid", idSet);
5860 videoUrl.AddOption("tagid", idTag);
5863 return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
5866 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5873 if (NULL == m_pDB.get()) return false;
5874 if (NULL == m_pDS.get()) return false;
5876 // parse the base path to get additional filters
5877 CVideoDbUrl videoUrl;
5878 Filter extFilter = filter;
5879 SortDescription sorting = sortDescription;
5880 if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
5885 CStdString strSQL = "select %s from movieview ";
5886 CStdString strSQLExtra;
5887 if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
5890 // Apply the limiting directly here if there's no special sorting but limiting
5891 if (extFilter.limit.empty() &&
5892 sorting.sortBy == SortByNone &&
5893 (sorting.limitStart > 0 || sorting.limitEnd > 0))
5895 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
5896 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
5899 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
5901 int iRowsFound = RunQuery(strSQL);
5902 if (iRowsFound <= 0)
5903 return iRowsFound == 0;
5905 // store the total value of items as a property
5906 if (total < iRowsFound)
5908 items.SetProperty("total", total);
5910 DatabaseResults results;
5911 results.reserve(iRowsFound);
5913 if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
5916 // get data from returned rows
5917 items.Reserve(results.size());
5918 const query_data &data = m_pDS->get_result_set().records;
5919 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
5921 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
5922 const dbiplus::sql_record* const record = data.at(targetRow);
5924 CVideoInfoTag movie = GetDetailsForMovie(record);
5925 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
5926 g_passwordManager.bMasterUser ||
5927 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
5929 CFileItemPtr pItem(new CFileItem(movie));
5931 CVideoDbUrl itemUrl = videoUrl;
5932 CStdString path = StringUtils::Format("%ld", movie.m_iDbId);
5933 itemUrl.AppendPath(path);
5934 pItem->SetPath(itemUrl.ToString());
5936 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
5947 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5952 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
5953 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
5954 const SortDescription &sortDescription /* = SortDescription() */)
5956 CVideoDbUrl videoUrl;
5957 if (!videoUrl.FromString(strBaseDir))
5961 videoUrl.AddOption("genreid", idGenre);
5962 else if (idStudio != -1)
5963 videoUrl.AddOption("studioid", idStudio);
5964 else if (idDirector != -1)
5965 videoUrl.AddOption("directorid", idDirector);
5966 else if (idYear != -1)
5967 videoUrl.AddOption("year", idYear);
5968 else if (idActor != -1)
5969 videoUrl.AddOption("actorid", idActor);
5970 else if (idTag != -1)
5971 videoUrl.AddOption("tagid", idTag);
5974 return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
5977 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5983 if (NULL == m_pDB.get()) return false;
5984 if (NULL == m_pDS.get()) return false;
5988 CStdString strSQL = "SELECT %s FROM tvshowview ";
5989 CVideoDbUrl videoUrl;
5990 CStdString strSQLExtra;
5991 Filter extFilter = filter;
5992 SortDescription sorting = sortDescription;
5993 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
5996 // Apply the limiting directly here if there's no special sorting but limiting
5997 if (extFilter.limit.empty() &&
5998 sorting.sortBy == SortByNone &&
5999 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6001 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6002 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6005 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6007 int iRowsFound = RunQuery(strSQL);
6008 if (iRowsFound <= 0)
6009 return iRowsFound == 0;
6011 // store the total value of items as a property
6012 if (total < iRowsFound)
6014 items.SetProperty("total", total);
6016 DatabaseResults results;
6017 results.reserve(iRowsFound);
6018 if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
6021 // get data from returned rows
6022 items.Reserve(results.size());
6023 const query_data &data = m_pDS->get_result_set().records;
6024 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6026 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6027 const dbiplus::sql_record* const record = data.at(targetRow);
6029 CFileItemPtr pItem(new CFileItem());
6030 CVideoInfoTag movie = GetDetailsForTvShow(record, false, pItem.get());
6031 if ((CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6032 g_passwordManager.bMasterUser ||
6033 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video"))) &&
6034 (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
6036 pItem->SetFromVideoInfoTag(movie);
6038 CVideoDbUrl itemUrl = videoUrl;
6039 CStdString path = StringUtils::Format("%ld/", record->at(0).get_asInt());
6040 itemUrl.AppendPath(path);
6041 pItem->SetPath(itemUrl.ToString());
6043 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6048 Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6056 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6061 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6063 if (maintainSortOrder)
6065 // save current sort order
6066 for (int i = 0; i < items.Size(); i++)
6067 items[i]->m_iprogramCount = i;
6072 case VIDEODB_CONTENT_TVSHOWS:
6075 items.Sort(SortBySortTitle, SortOrderAscending);
6078 while (i < items.Size())
6080 CFileItemPtr pItem = items.Get(i);
6081 CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6082 CStdString strFanArt = pItem->GetArt("fanart");
6085 bool bStacked = false;
6086 while (j < items.Size())
6088 CFileItemPtr jItem = items.Get(j);
6090 // matching title? append information
6091 if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6093 if (jItem->GetVideoInfoTag()->m_premiered !=
6094 pItem->GetVideoInfoTag()->m_premiered)
6101 // increment episode counts
6102 pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6103 pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6104 pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6105 pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6106 pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6108 // adjust lastplayed
6109 if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6110 pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6112 // check for fanart if not already set
6113 if (strFanArt.empty())
6114 strFanArt = jItem->GetArt("fanart");
6116 // remove duplicate entry
6119 // no match? exit loop
6123 // update playcount and fanart
6126 pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6127 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6128 if (!strFanArt.empty())
6129 pItem->SetArt("fanart", strFanArt);
6131 // increment i to j which is the next item
6136 // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6137 // so that if we eventually want to stack during scan we can utilize it.
6139 case VIDEODB_CONTENT_EPISODES:
6141 // sort by ShowTitle, Episode, Filename
6142 items.Sort(SortByEpisodeNumber, SortOrderAscending);
6145 while (i < items.Size())
6147 CFileItemPtr pItem = items.Get(i);
6148 CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6149 int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6150 int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6151 //CStdString strFanArt = pItem->GetArt("fanart");
6153 // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6154 CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6155 bool bDvdFolder = StringUtils::EndsWithNoCase(strFileNameAndPath, "video_ts.ifo");
6157 vector<CStdString> paths;
6158 paths.push_back(strFileNameAndPath);
6159 CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6162 int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6163 while (j < items.Size())
6165 CFileItemPtr jItem = items.Get(j);
6166 const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6167 CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6169 CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6170 // compare path, season, episode
6173 jTag->m_strPath.Equals(strPath) &&
6174 jTag->m_iSeason == iSeason &&
6175 jTag->m_iEpisode == iEpisode
6178 // keep checking to see if this is dvd folder
6181 bDvdFolder = StringUtils::EndsWithNoCase(jFileNameAndPath, "video_ts.ifo");
6182 // if we have a dvd folder, we stack differently
6185 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6187 paths.push_back(jFileNameAndPath);
6191 // increment playcount
6192 iPlayCount += jTag->m_playCount;
6194 // episodes dont have fanart yet
6195 //if (strFanArt.empty())
6196 // strFanArt = jItem->GetArt("fanart");
6198 paths.push_back(jFileNameAndPath);
6202 // remove duplicate entry
6206 // no match? exit loop
6210 // update playcount and fanart if we have a stacked entry
6211 if (paths.size() > 1)
6213 CStackDirectory dir;
6214 CStdString strStack;
6215 dir.ConstructStackPath(paths, strStack);
6216 pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6217 pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6218 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6220 // episodes dont have fanart yet
6221 //if (!strFanArt.empty())
6222 // pItem->SetArt("fanart", strFanArt);
6224 // increment i to j which is the next item
6230 // stack other types later
6234 if (maintainSortOrder)
6236 // restore original sort order - essential for smartplaylists
6237 items.Sort(SortByProgramCount, SortOrderAscending);
6241 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6243 CVideoDbUrl videoUrl;
6244 if (!videoUrl.FromString(strBaseDir))
6250 strIn = PrepareSQL("= %i", idShow);
6251 GetStackedTvShowList(idShow, strIn);
6253 videoUrl.AddOption("tvshowid", idShow);
6255 videoUrl.AddOption("season", idSeason);
6258 videoUrl.AddOption("genreid", idGenre);
6259 else if (idYear !=-1)
6260 videoUrl.AddOption("year", idYear);
6261 else if (idActor != -1)
6262 videoUrl.AddOption("actorid", idActor);
6264 else if (idYear != -1)
6265 videoUrl.AddOption("year", idYear);
6267 if (idDirector != -1)
6268 videoUrl.AddOption("directorid", idDirector);
6271 bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6273 if (idSeason == -1 && idShow != -1)
6274 { // add any linked movies
6276 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6277 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6278 CFileItemList movieItems;
6279 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
6281 if (movieItems.Size() > 0)
6282 items.Append(movieItems);
6288 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6295 if (NULL == m_pDB.get()) return false;
6296 if (NULL == m_pDS.get()) return false;
6300 CStdString strSQL = "select %s from episodeview ";
6301 CVideoDbUrl videoUrl;
6302 CStdString strSQLExtra;
6303 Filter extFilter = filter;
6304 SortDescription sorting = sortDescription;
6305 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6308 // Apply the limiting directly here if there's no special sorting but limiting
6309 if (extFilter.limit.empty() &&
6310 sorting.sortBy == SortByNone &&
6311 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6313 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6314 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6317 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6319 int iRowsFound = RunQuery(strSQL);
6320 if (iRowsFound <= 0)
6321 return iRowsFound == 0;
6323 // store the total value of items as a property
6324 if (total < iRowsFound)
6326 items.SetProperty("total", total);
6328 DatabaseResults results;
6329 results.reserve(iRowsFound);
6330 if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6333 // get data from returned rows
6334 items.Reserve(results.size());
6335 CLabelFormatter formatter("%H. %T", "");
6337 const query_data &data = m_pDS->get_result_set().records;
6338 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6340 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6341 const dbiplus::sql_record* const record = data.at(targetRow);
6343 CVideoInfoTag movie = GetDetailsForEpisode(record);
6344 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6345 g_passwordManager.bMasterUser ||
6346 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6348 CFileItemPtr pItem(new CFileItem(movie));
6349 formatter.FormatLabel(pItem.get());
6351 int idEpisode = record->at(0).get_asInt();
6353 CVideoDbUrl itemUrl = videoUrl;
6355 if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6356 path = StringUtils::Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6358 path = StringUtils::Format("%ld", idEpisode);
6359 itemUrl.AppendPath(path);
6360 pItem->SetPath(itemUrl.ToString());
6362 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6363 pItem->m_dateTime = movie.m_firstAired;
6364 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6375 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6380 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() */)
6382 CVideoDbUrl videoUrl;
6383 if (!videoUrl.FromString(strBaseDir))
6387 videoUrl.AddOption("genreid", idGenre);
6388 else if (idStudio != -1)
6389 videoUrl.AddOption("studioid", idStudio);
6390 else if (idDirector != -1)
6391 videoUrl.AddOption("directorid", idDirector);
6392 else if (idYear !=-1)
6393 videoUrl.AddOption("year", idYear);
6394 else if (idArtist != -1)
6395 videoUrl.AddOption("artistid", idArtist);
6396 else if (idTag != -1)
6397 videoUrl.AddOption("tagid", idTag);
6399 videoUrl.AddOption("albumid", idAlbum);
6402 return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6405 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6408 filter.order = "dateAdded desc, idMovie desc";
6409 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6410 return GetMoviesByWhere(strBaseDir, filter, items);
6413 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6416 filter.order = "dateAdded desc, idEpisode desc";
6417 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6418 return GetEpisodesByWhere(strBaseDir, filter, items, false);
6421 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6424 filter.order = "dateAdded desc, idMVideo desc";
6425 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6426 return GetMusicVideosByWhere(strBaseDir, filter, items);
6429 CStdString CVideoDatabase::GetGenreById(int id)
6431 return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6434 CStdString CVideoDatabase::GetCountryById(int id)
6436 return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6439 CStdString CVideoDatabase::GetSetById(int id)
6441 return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6444 CStdString CVideoDatabase::GetTagById(int id)
6446 return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6449 CStdString CVideoDatabase::GetPersonById(int id)
6451 return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6454 CStdString CVideoDatabase::GetStudioById(int id)
6456 return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6459 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6461 return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6464 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6466 return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6469 bool CVideoDatabase::HasSets() const
6473 if (NULL == m_pDB.get()) return false;
6474 if (NULL == m_pDS.get()) return false;
6476 m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6477 "JOIN sets ON sets.idSet = movieview.idSet "
6478 "GROUP BY movieview.idSet HAVING c>1");
6480 bool bResult = (m_pDS->num_rows() > 0);
6486 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6491 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6495 if (NULL == m_pDB.get()) return false;
6496 if (NULL == m_pDS2.get()) return false;
6498 // make sure we use m_pDS2, as this is called in loops using m_pDS
6499 CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6500 m_pDS2->query( strSQL.c_str() );
6504 result=m_pDS2->fv(0).get_asInt();
6511 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6516 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6519 sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6520 CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6523 return atoi(id.c_str());
6526 bool CVideoDatabase::HasContent()
6528 return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6529 HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6530 HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6533 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6535 bool result = false;
6538 if (NULL == m_pDB.get()) return false;
6539 if (NULL == m_pDS.get()) return false;
6542 if (type == VIDEODB_CONTENT_MOVIES)
6543 sql = "select count(1) from movie";
6544 else if (type == VIDEODB_CONTENT_TVSHOWS)
6545 sql = "select count(1) from tvshow";
6546 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6547 sql = "select count(1) from musicvideo";
6548 m_pDS->query( sql.c_str() );
6551 result = (m_pDS->fv(0).get_asInt() > 0);
6557 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6562 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6566 if (NULL == m_pDB.get()) return 0;
6567 if (NULL == m_pDS.get()) return 0;
6569 CStdString strSQL = StringUtils::Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6570 m_pDS->query( strSQL.c_str() );
6574 iResult = m_pDS->fv("nummovies").get_asInt();
6581 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6586 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6588 SScanSettings settings;
6589 return GetScraperForPath(strPath, settings);
6592 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6595 return GetScraperForPath(strPath, settings, dummy);
6598 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6600 foundDirectly = false;
6603 if (strPath.empty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6606 CStdString strPath2;
6608 if (URIUtils::IsMultiPath(strPath))
6609 strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6613 CStdString strPath1 = URIUtils::GetDirectory(strPath2);
6614 int idPath = GetPathId(strPath1);
6618 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);
6619 m_pDS->query( strSQL.c_str() );
6623 CONTENT_TYPE content = CONTENT_NONE;
6625 { // path is stored in db
6627 if (m_pDS->fv("path.exclude").get_asBool())
6629 settings.exclude = true;
6631 return ScraperPtr();
6633 settings.exclude = false;
6635 // try and ascertain scraper for this path
6636 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6637 StringUtils::ToLower(strcontent);
6638 content = TranslateContent(strcontent);
6640 //FIXME paths stored should not have empty strContent
6641 //assert(content != CONTENT_NONE);
6642 CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6645 if (!scraperID.empty() &&
6646 CAddonMgr::Get().GetAddon(scraperID, addon))
6648 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6650 return ScraperPtr();
6652 // store this path's content & settings
6653 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6654 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6655 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6656 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6660 if (content == CONTENT_NONE)
6661 { // this path is not saved in db
6662 // we must drill up until a scraper is configured
6663 CStdString strParent;
6664 while (URIUtils::GetParentPath(strPath1, strParent))
6668 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());
6669 m_pDS->query(strSQL.c_str());
6671 CONTENT_TYPE content = CONTENT_NONE;
6675 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6676 StringUtils::ToLower(strcontent);
6677 if (m_pDS->fv("path.exclude").get_asBool())
6679 settings.exclude = true;
6685 content = TranslateContent(strcontent);
6688 if (content != CONTENT_NONE &&
6689 CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6691 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6692 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6693 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6694 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6695 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6696 settings.exclude = false;
6700 strPath1 = strParent;
6705 if (!scraper || scraper->Content() == CONTENT_NONE)
6706 return ScraperPtr();
6708 if (scraper->Content() == CONTENT_TVSHOWS)
6710 settings.recurse = 0;
6711 if(settings.parent_name) // single show
6713 settings.parent_name_root = settings.parent_name = (iFound == 1);
6717 settings.parent_name_root = settings.parent_name = (iFound == 2);
6720 else if (scraper->Content() == CONTENT_MOVIES)
6722 settings.recurse = settings.recurse - (iFound-1);
6723 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6725 else if (scraper->Content() == CONTENT_MUSICVIDEOS)
6727 settings.recurse = settings.recurse - (iFound-1);
6728 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6733 return ScraperPtr();
6735 foundDirectly = (iFound == 1);
6740 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6742 return ScraperPtr();
6745 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
6747 SScanSettings settings;
6748 bool foundDirectly = false;
6749 ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
6752 if (scraper->Content() == CONTENT_TVSHOWS)
6753 { // check for episodes or seasons. Assumptions are:
6754 // 1. if episodes are in the path then we're in episodes.
6755 // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
6756 // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
6757 CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
6758 m_pDS->query( sql.c_str() );
6759 if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
6761 return foundDirectly ? "tvshows" : "seasons";
6763 return TranslateContent(scraper->Content());
6768 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
6774 if (NULL == m_pDB.get()) return;
6775 if (NULL == m_pDS.get()) return;
6777 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6778 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());
6780 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6781 m_pDS->query( strSQL.c_str() );
6783 while (!m_pDS->eof())
6785 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6786 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6787 *CMediaSourceSettings::Get().GetSources("video")))
6793 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6794 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6795 pItem->SetPath("videodb://movies/genres/"+ strDir);
6796 pItem->m_bIsFolder=true;
6804 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6808 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
6814 if (NULL == m_pDB.get()) return;
6815 if (NULL == m_pDS.get()) return;
6817 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6818 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());
6820 strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
6821 m_pDS->query( strSQL.c_str() );
6823 while (!m_pDS->eof())
6825 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6826 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6827 *CMediaSourceSettings::Get().GetSources("video")))
6833 CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
6834 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
6835 pItem->SetPath("videodb://movies/genres/"+ strDir);
6836 pItem->m_bIsFolder=true;
6844 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6848 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
6854 if (NULL == m_pDB.get()) return;
6855 if (NULL == m_pDS.get()) return;
6857 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6858 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());
6860 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6861 m_pDS->query( strSQL.c_str() );
6863 while (!m_pDS->eof())
6865 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6866 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6872 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6873 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6874 pItem->SetPath("videodb://tvshows/genres/"+ strDir);
6875 pItem->m_bIsFolder=true;
6883 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6887 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
6893 if (NULL == m_pDB.get()) return;
6894 if (NULL == m_pDS.get()) return;
6896 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6897 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());
6899 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());
6900 m_pDS->query( strSQL.c_str() );
6902 while (!m_pDS->eof())
6904 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6905 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6911 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6912 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6913 pItem->SetPath("videodb://movies/actors/"+ strDir);
6914 pItem->m_bIsFolder=true;
6922 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6926 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
6932 if (NULL == m_pDB.get()) return;
6933 if (NULL == m_pDS.get()) return;
6935 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6936 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());
6938 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());
6939 m_pDS->query( strSQL.c_str() );
6941 while (!m_pDS->eof())
6943 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6944 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6950 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6951 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6952 pItem->SetPath("videodb://tvshows/actors/"+ strDir);
6953 pItem->m_bIsFolder=true;
6961 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6965 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
6971 if (NULL == m_pDB.get()) return;
6972 if (NULL == m_pDS.get()) return;
6975 if (!strSearch.empty())
6976 strLike = "and actors.strActor like '%%%s%%'";
6977 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6978 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());
6980 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
6981 m_pDS->query( strSQL.c_str() );
6983 while (!m_pDS->eof())
6985 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6986 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
6992 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6993 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6994 pItem->SetPath("videodb://musicvideos/artists/"+ strDir);
6995 pItem->m_bIsFolder=true;
7003 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7007 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
7013 if (NULL == m_pDB.get()) return;
7014 if (NULL == m_pDS.get()) return;
7016 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7017 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());
7019 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
7020 m_pDS->query( strSQL.c_str() );
7022 while (!m_pDS->eof())
7024 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7025 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7031 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7032 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7033 pItem->SetPath("videodb://musicvideos/genres/"+ strDir);
7034 pItem->m_bIsFolder=true;
7042 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7046 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7052 if (NULL == m_pDB.get()) return;
7053 if (NULL == m_pDS.get()) return;
7055 strSQL = StringUtils::Format("SELECT DISTINCT"
7056 " musicvideo.c%02d,"
7057 " musicvideo.idMVideo,"
7062 " files.idFile=musicvideo.idFile"
7064 " path.idPath=files.idPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
7065 if (!strSearch.empty())
7066 strSQL += PrepareSQL(" WHERE musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM, strSearch.c_str());
7068 m_pDS->query( strSQL.c_str() );
7070 while (!m_pDS->eof())
7072 if (m_pDS->fv(0).get_asString().empty())
7078 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7079 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7085 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7086 CStdString strDir = StringUtils::Format("%ld", m_pDS->fv(1).get_asInt());
7087 pItem->SetPath("videodb://musicvideos/titles/"+ strDir);
7088 pItem->m_bIsFolder=false;
7096 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7100 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7106 if (NULL == m_pDB.get()) return;
7107 if (NULL == m_pDS.get()) return;
7109 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7110 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());
7112 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());
7113 m_pDS->query( strSQL.c_str() );
7115 while (!m_pDS->eof())
7117 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7118 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7124 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7125 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7127 pItem->SetPath("videodb://"+ strDir);
7128 pItem->m_bIsFolder=false;
7136 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7140 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7147 if (NULL == m_pDB.get()) return false;
7148 if (NULL == m_pDS.get()) return false;
7152 CStdString strSQL = "select %s from musicvideoview ";
7153 CVideoDbUrl videoUrl;
7154 CStdString strSQLExtra;
7155 Filter extFilter = filter;
7156 SortDescription sorting = sortDescription;
7157 if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7160 // Apply the limiting directly here if there's no special sorting but limiting
7161 if (extFilter.limit.empty() &&
7162 sorting.sortBy == SortByNone &&
7163 (sorting.limitStart > 0 || sorting.limitEnd > 0))
7165 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7166 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7169 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7171 int iRowsFound = RunQuery(strSQL);
7172 if (iRowsFound <= 0)
7173 return iRowsFound == 0;
7175 // store the total value of items as a property
7176 if (total < iRowsFound)
7178 items.SetProperty("total", total);
7180 DatabaseResults results;
7181 results.reserve(iRowsFound);
7182 if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7185 // get data from returned rows
7186 items.Reserve(results.size());
7187 // get songs from returned subtable
7188 const query_data &data = m_pDS->get_result_set().records;
7189 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7191 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7192 const dbiplus::sql_record* const record = data.at(targetRow);
7194 CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7195 if (!checkLocks || CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7196 g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
7198 CFileItemPtr item(new CFileItem(musicvideo));
7200 CVideoDbUrl itemUrl = videoUrl;
7201 CStdString path = StringUtils::Format("%ld", record->at(0).get_asInt());
7202 itemUrl.AppendPath(path);
7203 item->SetPath(itemUrl.ToString());
7205 item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7216 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7221 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7225 if (NULL == m_pDB.get()) return 0;
7226 if (NULL == m_pDS.get()) return 0;
7228 CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7229 if (!m_pDS->query(strSQL.c_str())) return 0;
7231 if (m_pDS->num_rows() == 0)
7236 songIDs.reserve(m_pDS->num_rows());
7237 while (!m_pDS->eof())
7239 songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7243 return songIDs.size();
7247 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7252 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7258 if (NULL == m_pDB.get()) return false;
7259 if (NULL == m_pDS.get()) return false;
7261 // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7262 CStdString strSQL = StringUtils::Format("select * from musicvideoview where %s", strWhere.c_str());
7263 strSQL += PrepareSQL(" order by RANDOM() limit 1");
7264 CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7266 if (!m_pDS->query(strSQL.c_str()))
7268 int iRowsFound = m_pDS->num_rows();
7269 if (iRowsFound != 1)
7274 *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7275 CStdString path = StringUtils::Format("videodb://musicvideos/titles/%ld",item->GetVideoInfoTag()->m_iDbId);
7276 item->SetPath(path);
7277 idSong = m_pDS->fv("idMVideo").get_asInt();
7278 item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7284 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7289 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7293 if (NULL == m_pDB.get()) return -1;
7294 if (NULL == m_pDS.get()) return -1;
7297 if (strAlbum.empty() && strTitle.empty())
7298 { // we want to return matching artists only
7299 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7300 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());
7302 strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7305 { // we want to return the matching musicvideo
7306 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7307 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());
7309 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());
7311 m_pDS->query( strSQL.c_str() );
7316 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7317 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7323 int lResult = m_pDS->fv(0).get_asInt();
7329 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7334 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7340 if (NULL == m_pDB.get()) return;
7341 if (NULL == m_pDS.get()) return;
7343 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7344 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());
7346 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());
7347 m_pDS->query( strSQL.c_str() );
7349 while (!m_pDS->eof())
7351 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7352 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7358 int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7359 int setId = m_pDS->fv("movie.idSet").get_asInt();
7360 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7362 if (setId <= 0 || !CSettings::Get().GetBool("videolibrary.groupmoviesets"))
7363 path = StringUtils::Format("videodb://movies/titles/%i", movieId);
7365 path = StringUtils::Format("videodb://movies/sets/%i/%i", setId, movieId);
7366 pItem->SetPath(path);
7367 pItem->m_bIsFolder=false;
7375 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7379 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7385 if (NULL == m_pDB.get()) return;
7386 if (NULL == m_pDS.get()) return;
7388 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7389 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());
7391 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());
7392 m_pDS->query( strSQL.c_str() );
7394 while (!m_pDS->eof())
7396 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7397 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7403 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7404 CStdString strDir = StringUtils::Format("tvshows/titles/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7406 pItem->SetPath("videodb://"+ strDir);
7407 pItem->m_bIsFolder=true;
7408 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7416 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7420 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7426 if (NULL == m_pDB.get()) return;
7427 if (NULL == m_pDS.get()) return;
7429 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7430 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());
7432 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());
7433 m_pDS->query( strSQL.c_str() );
7435 while (!m_pDS->eof())
7437 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7438 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7444 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7445 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());
7446 pItem->SetPath(path);
7447 pItem->m_bIsFolder=false;
7455 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7459 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7461 // Alternative searching - not quite as fast though due to
7462 // retrieving all information
7463 // 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()));
7464 // GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
7469 if (NULL == m_pDB.get()) return;
7470 if (NULL == m_pDS.get()) return;
7472 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7473 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());
7475 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());
7476 m_pDS->query( strSQL.c_str() );
7478 while (!m_pDS->eof())
7480 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7481 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7487 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7488 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7490 pItem->SetPath("videodb://"+ strDir);
7491 pItem->m_bIsFolder=false;
7499 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7503 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7505 // Alternative searching - not quite as fast though due to
7506 // retrieving all information
7508 // 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());
7509 // 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());
7510 // GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
7516 if (NULL == m_pDB.get()) return;
7517 if (NULL == m_pDS.get()) return;
7519 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7520 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());
7522 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());
7523 m_pDS->query( strSQL.c_str() );
7525 while (!m_pDS->eof())
7527 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7528 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7534 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7535 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());
7536 pItem->SetPath(path);
7537 pItem->m_bIsFolder=false;
7545 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7549 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7555 if (NULL == m_pDB.get()) return;
7556 if (NULL == m_pDS.get()) return;
7558 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7559 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());
7561 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());
7563 m_pDS->query( strSQL.c_str() );
7565 while (!m_pDS->eof())
7567 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7568 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7574 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7575 CStdString path = StringUtils::Format("videodb://movies/titles/%ld", m_pDS->fv(0).get_asInt());
7576 pItem->SetPath(path);
7577 pItem->m_bIsFolder=false;
7587 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7591 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7597 if (NULL == m_pDB.get()) return;
7598 if (NULL == m_pDS.get()) return;
7600 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7601 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());
7603 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());
7605 m_pDS->query( strSQL.c_str() );
7607 while (!m_pDS->eof())
7609 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7610 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7616 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7617 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7619 pItem->SetPath("videodb://movies/directors/"+ strDir);
7620 pItem->m_bIsFolder=true;
7628 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7632 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7638 if (NULL == m_pDB.get()) return;
7639 if (NULL == m_pDS.get()) return;
7641 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7642 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());
7644 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());
7646 m_pDS->query( strSQL.c_str() );
7648 while (!m_pDS->eof())
7650 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7651 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7657 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7658 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7660 pItem->SetPath("videodb://tvshows/studios/"+ strDir);
7661 pItem->m_bIsFolder=true;
7669 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7673 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7679 if (NULL == m_pDB.get()) return;
7680 if (NULL == m_pDS.get()) return;
7682 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7683 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());
7685 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());
7687 m_pDS->query( strSQL.c_str() );
7689 while (!m_pDS->eof())
7691 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7692 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7698 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7699 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7701 pItem->SetPath("videodb://musicvideos/albums/"+ strDir);
7702 pItem->m_bIsFolder=true;
7710 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7714 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7716 CGUIDialogProgress *progress=NULL;
7719 if (NULL == m_pDB.get()) return;
7720 if (NULL == m_pDS.get()) return;
7722 unsigned int time = XbmcThreads::SystemClockMillis();
7723 CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
7724 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
7728 // find all the files
7729 CStdString sql = "SELECT files.idFile, files.strFileName, path.strPath FROM files, path WHERE files.idPath = path.idPath";
7734 RollbackTransaction();
7735 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7739 CStdString strPaths;
7740 for (std::set<int>::const_iterator it = paths->begin(); it != paths->end(); ++it)
7741 strPaths += StringUtils::Format(",%i", *it);
7742 sql += PrepareSQL(" AND path.idPath IN (%s)", strPaths.substr(1).c_str());
7745 m_pDS->query(sql.c_str());
7746 if (m_pDS->num_rows() == 0) return;
7750 handle->SetTitle(g_localizeStrings.Get(700));
7751 handle->SetText("");
7753 else if (showProgress)
7755 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
7758 progress->SetHeading(700);
7759 progress->SetLine(0, "");
7760 progress->SetLine(1, 313);
7761 progress->SetLine(2, 330);
7762 progress->SetPercentage(0);
7763 progress->StartModal();
7764 progress->ShowProgressBar(true);
7768 std::string filesToTestForDelete;
7770 int total = m_pDS->num_rows();
7773 while (!m_pDS->eof())
7775 std::string path = m_pDS->fv("path.strPath").get_asString();
7776 std::string fileName = m_pDS->fv("files.strFileName").get_asString();
7777 CStdString fullPath;
7778 ConstructPath(fullPath, path, fileName);
7780 // get the first stacked file
7781 if (URIUtils::IsStack(fullPath))
7782 fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
7784 // remove optical, non-existing files
7785 if (URIUtils::IsOnDVD(fullPath) || !CFile::Exists(fullPath, false))
7786 filesToTestForDelete += m_pDS->fv("files.idFile").get_asString() + ",";
7788 if (handle == NULL && progress != NULL)
7790 int percentage = current * 100 / total;
7791 if (percentage > progress->GetPercentage())
7793 progress->SetPercentage(percentage);
7794 progress->Progress();
7796 if (progress->IsCanceled())
7800 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7804 else if (handle != NULL)
7805 handle->SetPercentage(current * 100 / (float)total);
7812 std::string filesToDelete;
7814 // Add any files that don't have a valid idPath entry to the filesToDelete list.
7815 m_pDS->query("SELECT files.idFile FROM files WHERE NOT EXISTS (SELECT 1 FROM path WHERE path.idPath = files.idPath)");
7816 while (!m_pDS->eof())
7818 string file = m_pDS->fv("files.idFile").get_asString() + ",";
7819 filesToTestForDelete += file;
7820 filesToDelete += file;
7826 std::map<int, bool> pathsDeleteDecisions;
7827 std::vector<int> movieIDs;
7828 std::vector<int> tvshowIDs;
7829 std::vector<int> episodeIDs;
7830 std::vector<int> musicVideoIDs;
7832 if (!filesToTestForDelete.empty())
7834 StringUtils::TrimRight(filesToTestForDelete, ",");
7836 movieIDs = CleanMediaType("movie", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7837 episodeIDs = CleanMediaType("episode", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7838 musicVideoIDs = CleanMediaType("musicvideo", filesToTestForDelete, pathsDeleteDecisions, filesToDelete, !showProgress);
7841 if (progress != NULL)
7843 progress->SetPercentage(100);
7844 progress->Progress();
7847 if (!filesToDelete.empty())
7849 filesToDelete = "(" + StringUtils::TrimRight(filesToDelete, ",") + ")";
7851 CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
7852 sql = "DELETE FROM files WHERE idFile IN " + filesToDelete;
7853 m_pDS->exec(sql.c_str());
7855 CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
7856 sql = "DELETE FROM streamdetails WHERE idFile IN " + filesToDelete;
7857 m_pDS->exec(sql.c_str());
7859 CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
7860 sql = "DELETE FROM bookmark WHERE idFile IN " + filesToDelete;
7861 m_pDS->exec(sql.c_str());
7863 CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
7864 sql = "DELETE FROM settings WHERE idFile IN " + filesToDelete;
7865 m_pDS->exec(sql.c_str());
7867 CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
7868 sql = "DELETE FROM stacktimes WHERE idFile IN " + filesToDelete;
7869 m_pDS->exec(sql.c_str());
7872 if (!movieIDs.empty())
7874 std::string moviesToDelete;
7875 for (std::vector<int>::const_iterator it = movieIDs.begin(); it != movieIDs.end(); ++it)
7876 moviesToDelete += StringUtils::Format("%i,", *it);
7877 moviesToDelete = "(" + StringUtils::TrimRight(moviesToDelete, ",") + ")";
7879 CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
7880 sql = "DELETE FROM movie WHERE idMovie IN " + moviesToDelete;
7881 m_pDS->exec(sql.c_str());
7883 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
7884 sql = "DELETE FROM actorlinkmovie WHERE idMovie IN " + moviesToDelete;
7885 m_pDS->exec(sql.c_str());
7887 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
7888 sql = "DELETE FROM directorlinkmovie WHERE idMovie IN " + moviesToDelete;
7889 m_pDS->exec(sql.c_str());
7891 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
7892 sql = "DELETE FROM writerlinkmovie WHERE idMovie IN " + moviesToDelete;
7893 m_pDS->exec(sql.c_str());
7895 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
7896 sql = "DELETE FROM genrelinkmovie WHERE idMovie IN " + moviesToDelete;
7897 m_pDS->exec(sql.c_str());
7899 CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
7900 sql = "DELETE FROM countrylinkmovie WHERE idMovie IN " + moviesToDelete;
7901 m_pDS->exec(sql.c_str());
7903 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
7904 sql = "DELETE FROM studiolinkmovie WHERE idMovie IN " + moviesToDelete;
7905 m_pDS->exec(sql.c_str());
7908 if (!episodeIDs.empty())
7910 std::string episodesToDelete;
7911 for (std::vector<int>::const_iterator it = episodeIDs.begin(); it != episodeIDs.end(); ++it)
7912 episodesToDelete += StringUtils::Format("%i,", *it);
7913 episodesToDelete = "(" + StringUtils::TrimRight(episodesToDelete, ",") + ")";
7915 CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
7916 sql = "DELETE FROM episode WHERE idEpisode IN " + episodesToDelete;
7917 m_pDS->exec(sql.c_str());
7919 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
7920 sql = "DELETE FROM actorlinkepisode WHERE idEpisode IN " + episodesToDelete;
7921 m_pDS->exec(sql.c_str());
7923 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
7924 sql = "DELETE FROM directorlinkepisode WHERE idEpisode IN " + episodesToDelete;
7925 m_pDS->exec(sql.c_str());
7927 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
7928 sql = "DELETE FROM writerlinkepisode WHERE idEpisode IN " + episodesToDelete;
7929 m_pDS->exec(sql.c_str());
7932 CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and have content set...", __FUNCTION__);
7933 sql = "SELECT path.idPath, path.strPath FROM path "
7934 "WHERE NOT ((strContent IS NULL OR strContent = '') "
7935 "AND (strSettings IS NULL OR strSettings = '') "
7936 "AND (strHash IS NULL OR strHash = '') "
7937 "AND (exclude IS NULL OR exclude != 1))";
7938 m_pDS->query(sql.c_str());
7940 while (!m_pDS->eof())
7942 std::map<int, bool>::const_iterator pathsDeleteDecision = pathsDeleteDecisions.find(m_pDS->fv(0).get_asInt());
7943 if ((pathsDeleteDecision != pathsDeleteDecisions.end() && pathsDeleteDecision->second) ||
7944 (pathsDeleteDecision == pathsDeleteDecisions.end() && !CDirectory::Exists(m_pDS->fv(1).get_asString(), false)))
7945 strIds += m_pDS->fv(0).get_asString() + ",";
7951 if (!strIds.empty())
7953 sql = PrepareSQL("DELETE FROM path WHERE idPath IN (%s)", StringUtils::TrimRight(strIds, ",").c_str());
7954 m_pDS->exec(sql.c_str());
7955 sql = "DELETE FROM tvshowlinkpath WHERE NOT EXISTS (SELECT 1 FROM path WHERE path.idPath = tvshowlinkpath.idPath)";
7956 m_pDS->exec(sql.c_str());
7959 CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
7960 sql = "DELETE FROM tvshow WHERE NOT EXISTS (SELECT 1 FROM tvshowlinkpath WHERE tvshowlinkpath.idShow = tvshow.idShow)";
7961 m_pDS->exec(sql.c_str());
7963 std::string tvshowsToDelete;
7964 sql = "SELECT tvshow.idShow FROM tvshow "
7965 "JOIN tvshowlinkpath ON tvshow.idShow = tvshowlinkpath.idShow "
7966 "JOIN path ON path.idPath = tvshowlinkpath.idPath "
7967 "WHERE NOT EXISTS (SELECT 1 FROM episode WHERE episode.idShow = tvshow.idShow) "
7968 "AND (path.strContent IS NULL OR path.strContent = '')";
7969 m_pDS->query(sql.c_str());
7970 while (!m_pDS->eof())
7972 tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
7973 tvshowsToDelete += m_pDS->fv(0).get_asString() + ",";
7977 if (!tvshowsToDelete.empty())
7979 sql = "DELETE FROM tvshow WHERE idShow IN (" + StringUtils::TrimRight(tvshowsToDelete, ",") + ")";
7980 m_pDS->exec(sql.c_str());
7982 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
7983 sql = "DELETE FROM actorlinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = actorlinktvshow.idShow)";
7984 m_pDS->exec(sql.c_str());
7986 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
7987 sql = "DELETE FROM directorlinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = directorlinktvshow.idShow)";
7988 m_pDS->exec(sql.c_str());
7990 CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
7991 sql = "DELETE FROM tvshowlinkpath WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = tvshowlinkpath.idShow)";
7992 m_pDS->exec(sql.c_str());
7994 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
7995 sql = "DELETE FROM genrelinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = genrelinktvshow.idShow)";
7996 m_pDS->exec(sql.c_str());
7998 CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
7999 sql = "DELETE FROM seasons WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = seasons.idShow)";
8000 m_pDS->exec(sql.c_str());
8002 CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
8003 sql = "DELETE FROM movielinktvshow WHERE NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.idShow = movielinktvshow.idShow)";
8004 m_pDS->exec(sql.c_str());
8005 sql = "DELETE FROM movielinktvshow WHERE NOT EXISTS (SELECT 1 FROM movie WHERE movie.idMovie = movielinktvshow.idMovie)";
8006 m_pDS->exec(sql.c_str());
8009 if (!musicVideoIDs.empty())
8011 std::string musicVideosToDelete;
8012 for (std::vector<int>::const_iterator it = musicVideoIDs.begin(); it != musicVideoIDs.end(); ++it)
8013 musicVideosToDelete += StringUtils::Format("%i,", *it);
8014 musicVideosToDelete = "(" + StringUtils::TrimRight(musicVideosToDelete, ",") + ")";
8016 CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8017 sql = "DELETE FROM musicvideo WHERE idMVideo IN " + musicVideosToDelete;
8018 m_pDS->exec(sql.c_str());
8020 CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8021 sql = "DELETE FROM artistlinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8022 m_pDS->exec(sql.c_str());
8024 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8025 sql = "DELETE FROM directorlinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8026 m_pDS->exec(sql.c_str());
8028 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8029 sql = "DELETE FROM genrelinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8030 m_pDS->exec(sql.c_str());
8032 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8033 sql = "DELETE FROM studiolinkmusicvideo WHERE idMVideo IN " + musicVideosToDelete;
8034 m_pDS->exec(sql.c_str());
8037 CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8038 sql = StringUtils::Format("DELETE FROM path "
8039 "WHERE (strContent IS NULL OR strContent = '') "
8040 "AND (strSettings IS NULL OR strSettings = '') "
8041 "AND (strHash IS NULL OR strHash = '') "
8042 "AND (exclude IS NULL OR exclude != 1) "
8043 "AND NOT EXISTS (SELECT 1 FROM files WHERE files.idPath = path.idPath) "
8044 "AND NOT EXISTS (SELECT 1 FROM tvshowlinkpath WHERE tvshowlinkpath.idPath = path.idPath) "
8045 "AND NOT EXISTS (SELECT 1 FROM movie WHERE movie.c%02d = path.idPath) "
8046 "AND NOT EXISTS (SELECT 1 FROM tvshow WHERE tvshow.c%02d = path.idPath) "
8047 "AND NOT EXISTS (SELECT 1 FROM episode WHERE episode.c%02d = path.idPath) "
8048 "AND NOT EXISTS (SELECT 1 FROM musicvideo WHERE musicvideo.c%02d = path.idPath)"
8049 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8050 m_pDS->exec(sql.c_str());
8052 CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8053 sql = "DELETE FROM genre "
8054 "WHERE NOT EXISTS (SELECT 1 FROM genrelinkmovie WHERE genrelinkmovie.idGenre = genre.idGenre) "
8055 "AND NOT EXISTS (SELECT 1 FROM genrelinktvshow WHERE genrelinktvshow.idGenre = genre.idGenre) "
8056 "AND NOT EXISTS (SELECT 1 FROM genrelinkmusicvideo WHERE genrelinkmusicvideo.idGenre = genre.idGenre)";
8057 m_pDS->exec(sql.c_str());
8059 CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8060 sql = "DELETE FROM country WHERE NOT EXISTS (SELECT 1 FROM countrylinkmovie WHERE countrylinkmovie.idCountry = country.idCountry)";
8061 m_pDS->exec(sql.c_str());
8063 CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8064 sql = "DELETE FROM actors "
8065 "WHERE NOT EXISTS (SELECT 1 FROM actorlinkmovie WHERE actorlinkmovie.idActor = actors.idActor) "
8066 "AND NOT EXISTS (SELECT 1 FROM directorlinkmovie WHERE directorlinkmovie.idDirector = actors.idActor) "
8067 "AND NOT EXISTS (SELECT 1 FROM writerlinkmovie WHERE writerlinkmovie.idWriter = actors.idActor) "
8068 "AND NOT EXISTS (SELECT 1 FROM actorlinktvshow WHERE actorlinktvshow.idActor = actors.idActor) "
8069 "AND NOT EXISTS (SELECT 1 FROM actorlinkepisode WHERE actorlinkepisode.idActor = actors.idActor) "
8070 "AND NOT EXISTS (SELECT 1 FROM directorlinktvshow WHERE directorlinktvshow.idDirector = actors.idActor) "
8071 "AND NOT EXISTS (SELECT 1 FROM directorlinkepisode WHERE directorlinkepisode.idDirector = actors.idActor) "
8072 "AND NOT EXISTS (SELECT 1 FROM writerlinkepisode WHERE writerlinkepisode.idWriter = actors.idActor) "
8073 "AND NOT EXISTS (SELECT 1 FROM artistlinkmusicvideo WHERE artistlinkmusicvideo.idArtist = actors.idActor) "
8074 "AND NOT EXISTS (SELECT 1 FROM directorlinkmusicvideo WHERE directorlinkmusicvideo.idDirector = actors.idActor)";
8075 m_pDS->exec(sql.c_str());
8077 CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8078 sql = "DELETE FROM studio "
8079 "WHERE NOT EXISTS (SELECT 1 FROM studiolinkmovie WHERE studiolinkmovie.idStudio = studio.idStudio) "
8080 "AND NOT EXISTS (SELECT 1 FROM studiolinkmusicvideo WHERE studiolinkmusicvideo.idStudio = studio.idStudio) "
8081 "AND NOT EXISTS (SELECT 1 FROM studiolinktvshow WHERE studiolinktvshow.idStudio = studio.idStudio)";
8082 m_pDS->exec(sql.c_str());
8084 CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8085 sql = "DELETE FROM sets WHERE NOT EXISTS (SELECT 1 FROM movie WHERE movie.idSet = sets.idSet)";
8086 m_pDS->exec(sql.c_str());
8088 CommitTransaction();
8091 handle->SetTitle(g_localizeStrings.Get(331));
8095 CUtil::DeleteVideoDatabaseDirectoryCache();
8097 time = XbmcThreads::SystemClockMillis() - time;
8098 CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8100 for (std::vector<int>::const_iterator it = movieIDs.begin(); it != movieIDs.end(); ++it)
8101 AnnounceRemove("movie", *it);
8103 for (std::vector<int>::const_iterator it = episodeIDs.begin(); it != episodeIDs.end(); ++it)
8104 AnnounceRemove("episode", *it);
8106 for (std::vector<int>::const_iterator it = tvshowIDs.begin(); it != tvshowIDs.end(); ++it)
8107 AnnounceRemove("tvshow", *it);
8109 for (std::vector<int>::const_iterator it = musicVideoIDs.begin(); it != musicVideoIDs.end(); ++it)
8110 AnnounceRemove("musicvideo", *it);
8114 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8115 RollbackTransaction();
8120 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8123 std::vector<int> CVideoDatabase::CleanMediaType(const std::string &mediaType, const std::string &cleanableFileIDs,
8124 std::map<int, bool> &pathsDeleteDecisions, std::string &deletedFileIDs, bool silent)
8126 std::vector<int> cleanedIDs;
8127 if (mediaType.empty() || cleanableFileIDs.empty())
8130 std::string table = mediaType;
8131 std::string idField;
8132 std::string parentPathIdField;
8133 bool isEpisode = false;
8134 if (mediaType == "movie")
8136 idField = "idMovie";
8137 parentPathIdField = StringUtils::Format("%s.c%02d", table.c_str(), VIDEODB_ID_PARENTPATHID);
8139 else if (mediaType == "episode")
8141 idField = "idEpisode";
8142 parentPathIdField = StringUtils::Format("tvshow.c%02d", VIDEODB_ID_TV_PARENTPATHID);
8145 else if (mediaType == "musicvideo")
8147 idField = "idMVideo";
8148 parentPathIdField = StringUtils::Format("%s.c%02d", table.c_str(), VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
8153 // now grab them media items
8154 std::string sql = PrepareSQL("SELECT %s.%s, %s.idFile, %s, path.idPath, parentPath.strPath, parentPath.useFolderNames FROM %s "
8155 "JOIN files ON files.idFile = %s.idFile "
8156 "JOIN path ON path.idPath = files.idPath ",
8157 table.c_str(), idField.c_str(), table.c_str(), parentPathIdField.c_str(), table.c_str(),
8161 sql += "JOIN tvshow ON tvshow.idShow = episode.idShow ";
8163 sql += PrepareSQL("JOIN path as parentPath ON parentPath.idPath = %s "
8164 "WHERE %s.idFile IN (%s)",
8165 parentPathIdField.c_str(),
8166 table.c_str(), cleanableFileIDs.c_str());
8168 // map of parent path ID to boolean pair (if not exists and user choice)
8169 std::map<int, std::pair<bool, bool> > parentPathsDeleteDecisions;
8170 m_pDS->query(sql.c_str());
8171 while (!m_pDS->eof())
8173 int parentPathID = m_pDS->fv(2).get_asInt();
8174 std::map<int, std::pair<bool, bool> >::const_iterator parentPathsDeleteDecision = parentPathsDeleteDecisions.find(parentPathID);
8176 if (parentPathsDeleteDecision == parentPathsDeleteDecisions.end())
8178 std::string parentPath = m_pDS->fv(4).get_asString();
8179 bool parentPathNotExists = !CDirectory::Exists(parentPath, false);
8180 // if the parent path exists, the file will be deleted without asking
8181 // if the parent path doesn't exist, ask the user whether to remove all items it contained
8182 if (parentPathNotExists)
8184 // in silent mode assume that the files are just temporarily missing
8189 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
8190 if (pDialog != NULL)
8192 CURL parentUrl(parentPath);
8193 pDialog->SetHeading(15012);
8194 pDialog->SetText(StringUtils::Format(g_localizeStrings.Get(15013), parentUrl.GetWithoutUserDetails().c_str()));
8195 pDialog->SetChoice(0, 15015);
8196 pDialog->SetChoice(1, 15014);
8198 //send message and wait for user input
8199 ThreadMessage tMsg = { TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_YES_NO, (unsigned int)g_windowManager.GetActiveWindow() };
8200 CApplicationMessenger::Get().SendMessage(tMsg, true);
8202 del = !pDialog->IsConfirmed();
8207 parentPathsDeleteDecisions.insert(make_pair(parentPathID, make_pair(parentPathNotExists, del)));
8209 // the only reason not to delete the file is if the parent path doesn't
8210 // exist and the user decided to delete all the items it contained
8211 else if (parentPathsDeleteDecision->second.first &&
8212 !parentPathsDeleteDecision->second.second)
8215 if (m_pDS->fv(5).get_asBool())
8216 pathsDeleteDecisions.insert(make_pair(m_pDS->fv(3).get_asInt(), del));
8220 deletedFileIDs += m_pDS->fv(1).get_asString() + ",";
8221 cleanedIDs.push_back(m_pDS->fv(0).get_asInt());
8231 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8234 CFileItemList items;
8235 GetTvShowsByWhere("videodb://tvshows/titles/", "", items);
8236 CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8237 CDirectory::Create(showPath);
8238 for (int i = 0; i < items.Size(); i++)
8240 // create a folder in this directory
8241 CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8242 CStdString TVFolder = URIUtils::AddFileToFolder(showPath, showName);
8243 if (CDirectory::Create(TVFolder))
8244 { // right - grab the episodes and dump them as well
8245 CFileItemList episodes;
8246 Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8247 GetEpisodesByWhere("videodb://tvshows/titles/", filter, episodes);
8248 for (int i = 0; i < episodes.Size(); i++)
8250 CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8251 CStdString episode = StringUtils::Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8253 CStdString episodePath = URIUtils::AddFileToFolder(TVFolder, episode);
8255 if (file.OpenForWrite(episodePath))
8262 GetMoviesByWhere("videodb://movies/titles/", "", items);
8263 CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8264 CDirectory::Create(moviePath);
8265 for (int i = 0; i < items.Size(); i++)
8267 CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8268 CStdString movie = StringUtils::Format("%s.avi", tag->m_strTitle.c_str());
8270 if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8275 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8277 CGUIDialogProgress *progress=NULL;
8280 if (NULL == m_pDB.get()) return;
8281 if (NULL == m_pDS.get()) return;
8282 if (NULL == m_pDS2.get()) return;
8284 // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8285 auto_ptr<Dataset> pDS;
8286 pDS.reset(m_pDB->CreateDataset());
8287 if (NULL == pDS.get()) return;
8289 auto_ptr<Dataset> pDS2;
8290 pDS2.reset(m_pDB->CreateDataset());
8291 if (NULL == pDS2.get()) return;
8293 // if we're exporting to a single folder, we export thumbs as well
8294 CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8295 CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8296 CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8297 CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8298 CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8299 CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8305 CDirectory::Remove(exportRoot);
8306 CDirectory::Create(exportRoot);
8307 CDirectory::Create(actorsDir);
8308 CDirectory::Create(moviesDir);
8309 CDirectory::Create(musicvideosDir);
8310 CDirectory::Create(tvshowsDir);
8313 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8315 CStdString sql = "select * from movieview";
8317 m_pDS->query(sql.c_str());
8321 progress->SetHeading(647);
8322 progress->SetLine(0, 650);
8323 progress->SetLine(1, "");
8324 progress->SetLine(2, "");
8325 progress->SetPercentage(0);
8326 progress->StartModal();
8327 progress->ShowProgressBar(true);
8330 int total = m_pDS->num_rows();
8333 // create our xml document
8334 CXBMCTinyXML xmlDoc;
8335 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8336 xmlDoc.InsertEndChild(decl);
8337 TiXmlNode *pMain = NULL;
8342 TiXmlElement xmlMainElement("videodb");
8343 pMain = xmlDoc.InsertEndChild(xmlMainElement);
8344 XMLUtils::SetInt(pMain,"version", GetExportVersion());
8347 while (!m_pDS->eof())
8349 CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8350 // strip paths to make them relative
8351 if (StringUtils::StartsWith(movie.m_strTrailer, movie.m_strPath))
8352 movie.m_strTrailer = movie.m_strTrailer.substr(movie.m_strPath.size());
8353 map<string, string> artwork;
8354 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8356 TiXmlElement additionalNode("art");
8357 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8358 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8359 movie.Save(pMain, "movie", true, &additionalNode);
8362 movie.Save(pMain, "movie", !singleFiles);
8364 // reset old skip state
8369 progress->SetLine(1, movie.m_strTitle);
8370 progress->SetPercentage(current * 100 / total);
8371 progress->Progress();
8372 if (progress->IsCanceled())
8380 CFileItem item(movie.m_strFileNameAndPath,false);
8381 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8383 if (!item.Exists(false))
8385 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8390 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8392 if (item.IsOpticalMediaFile())
8394 nfoFile = URIUtils::AddFileToFolder(
8395 URIUtils::GetParentPath(nfoFile),
8396 URIUtils::GetFileName(nfoFile));
8399 if (overwrite || !CFile::Exists(nfoFile, false))
8401 if(!xmlDoc.SaveFile(nfoFile))
8403 CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8404 bSkip = ExportSkipEntry(nfoFile);
8421 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8422 xmlDoc.InsertEndChild(decl);
8425 if (images && !bSkip)
8429 CStdString strFileName(movie.m_strTitle);
8430 if (movie.m_iYear > 0)
8431 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8432 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8434 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8436 CStdString savedThumb = item.GetLocalArt(i->first, false);
8437 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8440 ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8447 // find all musicvideos
8448 sql = "select * from musicvideoview";
8450 m_pDS->query(sql.c_str());
8452 total = m_pDS->num_rows();
8455 while (!m_pDS->eof())
8457 CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8458 map<string, string> artwork;
8459 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8461 TiXmlElement additionalNode("art");
8462 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8463 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8464 movie.Save(pMain, "musicvideo", true, &additionalNode);
8467 movie.Save(pMain, "musicvideo", !singleFiles);
8469 // reset old skip state
8474 progress->SetLine(1, movie.m_strTitle);
8475 progress->SetPercentage(current * 100 / total);
8476 progress->Progress();
8477 if (progress->IsCanceled())
8485 CFileItem item(movie.m_strFileNameAndPath,false);
8486 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8488 if (!item.Exists(false))
8490 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8495 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8497 if (overwrite || !CFile::Exists(nfoFile, false))
8499 if(!xmlDoc.SaveFile(nfoFile))
8501 CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8502 bSkip = ExportSkipEntry(nfoFile);
8519 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8520 xmlDoc.InsertEndChild(decl);
8522 if (images && !bSkip)
8526 CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8527 if (movie.m_iYear > 0)
8528 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8529 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8531 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8533 CStdString savedThumb = item.GetLocalArt(i->first, false);
8534 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8542 // repeat for all tvshows
8543 sql = "SELECT * FROM tvshowview";
8544 m_pDS->query(sql.c_str());
8546 total = m_pDS->num_rows();
8549 while (!m_pDS->eof())
8551 CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8553 map<int, map<string, string> > seasonArt;
8554 GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8556 map<string, string> artwork;
8557 if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8559 TiXmlElement additionalNode("art");
8560 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8561 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8562 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8564 TiXmlElement seasonNode("season");
8565 seasonNode.SetAttribute("num", i->first);
8566 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8567 XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8568 additionalNode.InsertEndChild(seasonNode);
8570 tvshow.Save(pMain, "tvshow", true, &additionalNode);
8573 tvshow.Save(pMain, "tvshow", !singleFiles);
8575 // reset old skip state
8580 progress->SetLine(1, tvshow.m_strTitle);
8581 progress->SetPercentage(current * 100 / total);
8582 progress->Progress();
8583 if (progress->IsCanceled())
8591 // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8592 if (URIUtils::IsMultiPath(tvshow.m_strPath))
8593 tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8595 CFileItem item(tvshow.m_strPath, true);
8596 if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8598 if (!item.Exists(false))
8600 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8605 CStdString nfoFile = URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo");
8607 if (overwrite || !CFile::Exists(nfoFile, false))
8609 if(!xmlDoc.SaveFile(nfoFile))
8611 CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8612 bSkip = ExportSkipEntry(nfoFile);
8629 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8630 xmlDoc.InsertEndChild(decl);
8632 if (images && !bSkip)
8635 item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8637 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8639 CStdString savedThumb = item.GetLocalArt(i->first, true);
8640 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8644 ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8646 // export season thumbs
8647 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8651 seasonThumb = "season-all";
8652 else if (i->first == 0)
8653 seasonThumb = "season-specials";
8655 seasonThumb = StringUtils::Format("season%02i", i->first);
8656 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8658 CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8659 if (!i->second.empty())
8660 CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8665 // now save the episodes from this show
8666 sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8667 pDS->query(sql.c_str());
8668 CStdString showDir(item.GetPath());
8672 CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8673 map<string, string> artwork;
8674 if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8676 TiXmlElement additionalNode("art");
8677 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8678 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8679 episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8681 else if (!singleFiles)
8682 episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8684 episode.Save(pMain, "episodedetails", !singleFiles);
8686 // multi-episode files need dumping to the same XML
8687 while (singleFiles && !pDS->eof() &&
8688 episode.m_iFileId == pDS->fv("idFile").get_asInt())
8690 episode = GetDetailsForEpisode(pDS, true);
8691 episode.Save(pMain, "episodedetails", !singleFiles);
8695 // reset old skip state
8698 CFileItem item(episode.m_strFileNameAndPath, false);
8699 if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8701 if (!item.Exists(false))
8703 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8708 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8710 if (overwrite || !CFile::Exists(nfoFile, false))
8712 if(!xmlDoc.SaveFile(nfoFile))
8714 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8715 bSkip = ExportSkipEntry(nfoFile);
8732 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8733 xmlDoc.InsertEndChild(decl);
8736 if (images && !bSkip)
8740 CStdString epName = StringUtils::Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8741 item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8743 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8745 CStdString savedThumb = item.GetLocalArt(i->first, false);
8746 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8749 ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8758 if (singleFiles && progress)
8760 progress->SetPercentage(100);
8761 progress->Progress();
8766 // now dump path info
8767 set<CStdString> paths;
8769 TiXmlElement xmlPathElement("paths");
8770 TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8771 for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8773 bool foundDirectly = false;
8774 SScanSettings settings;
8775 ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8776 if (info && foundDirectly)
8778 TiXmlElement xmlPathElement2("path");
8779 TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8780 XMLUtils::SetString(pPath,"url", *iter);
8781 XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8782 XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8783 XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8784 XMLUtils::SetString(pPath,"scraperpath", info->ID());
8787 xmlDoc.SaveFile(xmlFile);
8792 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8799 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8801 CStdString strPath(strDir);
8804 strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8805 if (!CDirectory::Exists(strPath))
8807 CDirectory::Create(strPath);
8808 CFile::SetHidden(strPath, true);
8812 for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8815 item.SetLabel(iter->strName);
8816 if (!iter->thumb.empty())
8818 CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8819 CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8824 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8826 CStdString strParent;
8827 URIUtils::GetParentPath(nfoFile,strParent);
8828 CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8830 bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8833 CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8835 CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8840 void CVideoDatabase::ImportFromXML(const CStdString &path)
8842 CGUIDialogProgress *progress=NULL;
8845 if (NULL == m_pDB.get()) return;
8846 if (NULL == m_pDS.get()) return;
8848 CXBMCTinyXML xmlDoc;
8849 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
8852 TiXmlElement *root = xmlDoc.RootElement();
8855 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8858 progress->SetHeading(648);
8859 progress->SetLine(0, 649);
8860 progress->SetLine(1, 330);
8861 progress->SetLine(2, "");
8862 progress->SetPercentage(0);
8863 progress->StartModal();
8864 progress->ShowProgressBar(true);
8868 XMLUtils::GetInt(root, "version", iVersion);
8870 CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
8872 TiXmlElement *movie = root->FirstChildElement();
8875 // first count the number of items...
8878 if (strnicmp(movie->Value(), "movie", 5)==0 ||
8879 strnicmp(movie->Value(), "tvshow", 6)==0 ||
8880 strnicmp(movie->Value(), "musicvideo",10)==0 )
8882 movie = movie->NextSiblingElement();
8885 CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
8886 CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
8887 CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
8888 CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
8889 CVideoInfoScanner scanner;
8890 // add paths first (so we have scraper settings available)
8891 TiXmlElement *path = root->FirstChildElement("paths");
8892 path = path->FirstChildElement();
8896 if (XMLUtils::GetString(path,"url",strPath) && !strPath.empty())
8900 if (XMLUtils::GetString(path,"content", content) && !content.empty())
8901 { // check the scraper exists, if so store the path
8904 XMLUtils::GetString(path,"scraperpath",id);
8905 if (CAddonMgr::Get().GetAddon(id, addon))
8907 SScanSettings settings;
8908 ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
8909 // FIXME: scraper settings are not exported?
8910 scraper->SetPathSettings(TranslateContent(content), "");
8911 XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
8912 XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
8913 SetScraperForPath(strPath,scraper,settings);
8916 path = path->NextSiblingElement();
8918 movie = root->FirstChildElement();
8922 if (strnicmp(movie->Value(), "movie", 5) == 0)
8925 CFileItem item(info);
8926 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
8927 CStdString filename = info.m_strTitle;
8928 if (info.m_iYear > 0)
8929 filename += StringUtils::Format("_%i", info.m_iYear);
8930 CFileItem artItem(item);
8931 artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
8932 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8933 item.SetArt(artItem.GetArt());
8934 scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
8937 else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
8940 CFileItem item(info);
8941 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
8942 CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
8943 if (info.m_iYear > 0)
8944 filename += StringUtils::Format("_%i", info.m_iYear);
8945 CFileItem artItem(item);
8946 artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
8947 scanner.GetArtwork(&artItem, CONTENT_MUSICVIDEOS, useFolders, true, actorsDir);
8948 item.SetArt(artItem.GetArt());
8949 scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
8952 else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
8954 // load the TV show in. NOTE: This deletes all episodes under the TV Show, which may not be
8955 // what we desire. It may make better sense to only delete (or even better, update) the show information
8957 URIUtils::AddSlashAtEnd(info.m_strPath);
8958 DeleteTvShow(info.m_strPath);
8959 CFileItem showItem(info);
8960 bool useFolders = info.m_basePath.empty() ? LookupByFolders(showItem.GetPath(), true) : false;
8961 CFileItem artItem(showItem);
8962 CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
8963 artItem.SetPath(artPath);
8964 scanner.GetArtwork(&artItem, CONTENT_TVSHOWS, useFolders, true, actorsDir);
8965 showItem.SetArt(artItem.GetArt());
8966 int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
8968 map<int, map<string, string> > seasonArt;
8969 artItem.GetVideoInfoTag()->m_strPath = artPath;
8970 scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
8971 for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8973 int seasonID = AddSeason(showID, i->first);
8974 SetArtForItem(seasonID, "season", i->second);
8977 // now load the episodes
8978 TiXmlElement *episode = movie->FirstChildElement("episodedetails");
8981 // no need to delete the episode info, due to the above deletion
8984 CFileItem item(info);
8985 CStdString filename = StringUtils::Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
8986 CFileItem artItem(item);
8987 artItem.SetPath(GetSafeFile(artPath, filename));
8988 scanner.GetArtwork(&artItem, CONTENT_TVSHOWS, useFolders, true, actorsDir);
8989 item.SetArt(artItem.GetArt());
8990 scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
8991 episode = episode->NextSiblingElement("episodedetails");
8994 movie = movie->NextSiblingElement();
8995 if (progress && total)
8997 progress->SetPercentage(current * 100 / total);
8998 progress->SetLine(2, info.m_strTitle);
8999 progress->Progress();
9000 if (progress->IsCanceled())
9010 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
9016 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
9018 if (!node) return false;
9019 const TiXmlNode *art = node->FirstChild();
9020 while (art && art->FirstChild())
9022 artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
9023 art = art->NextSibling();
9025 return !artwork.empty();
9028 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
9030 if (URIUtils::IsStack(strFileName) ||
9031 URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
9032 strDest = strFileName;
9034 strDest = URIUtils::AddFileToFolder(strPath, strFileName);
9037 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
9039 if (URIUtils::IsStack(strFileNameAndPath) || StringUtils::StartsWithNoCase(strFileNameAndPath, "rar://") || StringUtils::StartsWithNoCase(strFileNameAndPath, "zip://"))
9041 URIUtils::GetParentPath(strFileNameAndPath,strPath);
9042 strFileName = strFileNameAndPath;
9044 else if (URIUtils::IsPlugin(strFileNameAndPath))
9046 CURL url(strFileNameAndPath);
9047 strPath = url.GetWithoutFilename();
9048 strFileName = strFileNameAndPath;
9051 URIUtils::Split(strFileNameAndPath,strPath, strFileName);
9054 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
9056 SScanSettings settings;
9058 ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
9059 SetPathHash(strPath,"");
9062 if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
9064 if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
9066 CStdString strParent;
9067 URIUtils::GetParentPath(strPath,strParent);
9068 SetPathHash(strParent,"");
9073 bool CVideoDatabase::CommitTransaction()
9075 if (CDatabase::CommitTransaction())
9076 { // number of items in the db has likely changed, so recalculate
9077 g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
9078 g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
9079 g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
9085 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, int dbField, const std::string &strValue)
9090 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9093 string strTable, strField;
9094 if (type == VIDEODB_CONTENT_MOVIES)
9097 strField = "idMovie";
9099 else if (type == VIDEODB_CONTENT_TVSHOWS)
9101 strTable = "tvshow";
9102 strField = "idShow";
9104 else if (type == VIDEODB_CONTENT_EPISODES)
9106 strTable = "episode";
9107 strField = "idEpisode";
9109 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
9111 strTable = "musicvideo";
9112 strField = "idMVideo";
9115 if (strTable.empty())
9118 return SetSingleValue(strTable, StringUtils::Format("c%02u", dbField), strValue, strField, dbId);
9122 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
9127 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, Field dbField, const std::string &strValue)
9129 MediaType mediaType = DatabaseUtils::MediaTypeFromVideoContentType(type);
9130 if (mediaType == MediaTypeNone)
9133 int dbFieldIndex = DatabaseUtils::GetField(dbField, mediaType);
9134 if (dbFieldIndex < 0)
9137 return SetSingleValue(type, dbId, dbFieldIndex, strValue);
9140 bool CVideoDatabase::SetSingleValue(const std::string &table, const std::string &fieldName, const std::string &strValue,
9141 const std::string &conditionName /* = "" */, int conditionValue /* = -1 */)
9143 if (table.empty() || fieldName.empty())
9149 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9152 sql = PrepareSQL("UPDATE %s SET %s='%s'", table.c_str(), fieldName.c_str(), strValue.c_str());
9153 if (!conditionName.empty())
9154 sql += PrepareSQL(" WHERE %s=%u", conditionName.c_str(), conditionValue);
9155 if (m_pDS->exec(sql.c_str()) == 0)
9160 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, sql.c_str());
9165 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
9167 CStdString safeThumb(name);
9168 StringUtils::Replace(safeThumb, ' ', '_');
9169 return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
9172 void CVideoDatabase::AnnounceRemove(std::string content, int id)
9175 data["type"] = content;
9177 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9180 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9183 data["type"] = content;
9185 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9188 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9190 CStdString path(strPath);
9192 if(URIUtils::IsMultiPath(path))
9194 vector<CStdString> paths;
9195 CMultiPathDirectory::GetPaths(path, paths);
9197 for(unsigned i=0;i<paths.size();i++)
9198 GetItemsForPath(content, paths[i], items);
9200 return items.Size() > 0;
9203 int pathID = GetPathId(path);
9207 if (content == "movies")
9209 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9210 GetMoviesByWhere("videodb://movies/titles/", filter, items);
9212 else if (content == "episodes")
9214 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9215 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
9217 else if (content == "tvshows")
9219 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9220 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
9222 else if (content == "musicvideos")
9224 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9225 GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
9227 for (int i = 0; i < items.Size(); i++)
9228 items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9229 return items.Size() > 0;
9232 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9234 if (!videoUrl.IsValid())
9237 std::string type = videoUrl.GetType();
9238 std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9239 const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9240 CUrlOptions::UrlOptions::const_iterator option;
9242 if (type == "movies")
9244 option = options.find("genreid");
9245 if (option != options.end())
9247 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9248 filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9251 option = options.find("genre");
9252 if (option != options.end())
9254 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9255 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9258 option = options.find("countryid");
9259 if (option != options.end())
9261 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9262 filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9265 option = options.find("country");
9266 if (option != options.end())
9268 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9269 filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9272 option = options.find("studioid");
9273 if (option != options.end())
9275 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9276 filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9279 option = options.find("studio");
9280 if (option != options.end())
9282 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9283 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9286 option = options.find("directorid");
9287 if (option != options.end())
9289 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9290 filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9293 option = options.find("director");
9294 if (option != options.end())
9296 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9297 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9300 option = options.find("year");
9301 if (option != options.end())
9302 filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9304 option = options.find("actorid");
9305 if (option != options.end())
9307 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9308 filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9311 option = options.find("actor");
9312 if (option != options.end())
9314 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9315 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9318 option = options.find("setid");
9319 if (option != options.end())
9320 filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9322 option = options.find("set");
9323 if (option != options.end())
9325 filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9326 filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9329 option = options.find("tagid");
9330 if (option != options.end())
9332 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9333 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9336 option = options.find("tag");
9337 if (option != options.end())
9339 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9340 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9343 else if (type == "tvshows")
9345 if (itemType == "tvshows")
9347 option = options.find("genreid");
9348 if (option != options.end())
9350 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9351 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9354 option = options.find("genre");
9355 if (option != options.end())
9357 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9358 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9361 option = options.find("studioid");
9362 if (option != options.end())
9364 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9365 filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9368 option = options.find("studio");
9369 if (option != options.end())
9371 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9372 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9375 option = options.find("directorid");
9376 if (option != options.end())
9378 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9379 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9382 option = options.find("year");
9383 if (option != options.end())
9384 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9386 option = options.find("actorid");
9387 if (option != options.end())
9389 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9390 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9393 option = options.find("actor");
9394 if (option != options.end())
9396 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9397 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9400 option = options.find("tagid");
9401 if (option != options.end())
9403 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9404 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9407 option = options.find("tag");
9408 if (option != options.end())
9410 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9411 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9414 else if (itemType == "seasons")
9416 option = options.find("genreid");
9417 if (option != options.end())
9419 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9420 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9423 option = options.find("directorid");
9424 if (option != options.end())
9426 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9427 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9430 option = options.find("year");
9431 if (option != options.end())
9432 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9434 option = options.find("actorid");
9435 if (option != options.end())
9437 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9438 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9441 else if (itemType == "episodes")
9444 option = options.find("tvshowid");
9445 if (option != options.end())
9446 idShow = (int)option->second.asInteger();
9449 option = options.find("season");
9450 if (option != options.end())
9451 season = (int)option->second.asInteger();
9453 CStdString strIn = PrepareSQL("= %i", idShow);
9454 GetStackedTvShowList(idShow, strIn);
9458 bool condition = false;
9460 option = options.find("genreid");
9461 if (option != options.end())
9464 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9465 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9468 option = options.find("genre");
9469 if (option != options.end())
9472 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9473 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9476 option = options.find("directorid");
9477 if (option != options.end())
9480 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9481 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9484 option = options.find("director");
9485 if (option != options.end())
9488 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9489 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9492 option = options.find("year");
9493 if (option != options.end())
9496 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9499 option = options.find("actorid");
9500 if (option != options.end())
9503 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9504 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9507 option = options.find("actor");
9508 if (option != options.end())
9511 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9512 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9516 filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9520 if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9521 filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9523 filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9524 VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9529 option = options.find("year");
9530 if (option != options.end())
9531 filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9533 option = options.find("directorid");
9534 if (option != options.end())
9536 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9537 filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9540 option = options.find("director");
9541 if (option != options.end())
9543 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9544 filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9549 else if (type == "musicvideos")
9551 option = options.find("genreid");
9552 if (option != options.end())
9554 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9555 filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9558 option = options.find("genre");
9559 if (option != options.end())
9561 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9562 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9565 option = options.find("studioid");
9566 if (option != options.end())
9568 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9569 filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9572 option = options.find("studio");
9573 if (option != options.end())
9575 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9576 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9579 option = options.find("directorid");
9580 if (option != options.end())
9582 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9583 filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9586 option = options.find("director");
9587 if (option != options.end())
9589 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9590 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9593 option = options.find("year");
9594 if (option != options.end())
9595 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9597 option = options.find("artistid");
9598 if (option != options.end())
9600 if (itemType != "albums")
9601 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9602 filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9605 option = options.find("artist");
9606 if (option != options.end())
9608 if (itemType != "albums")
9610 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9611 filter.AppendJoin(PrepareSQL("join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9613 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9616 option = options.find("albumid");
9617 if (option != options.end())
9618 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()));
9620 option = options.find("tagid");
9621 if (option != options.end())
9623 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9624 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9627 option = options.find("tag");
9628 if (option != options.end())
9630 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9631 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9637 option = options.find("xsp");
9638 if (option != options.end())
9641 if (!xsp.LoadFromJson(option->second.asString()))
9644 // check if the filter playlist matches the item type
9645 if (xsp.GetType() == itemType ||
9646 (xsp.GetGroup() == itemType && !xsp.IsGroupMixed()) ||
9647 // handle episode listings with videodb://tvshows/titles/ which get the rest
9648 // of the path (season and episodeid) appended later
9649 (xsp.GetType() == "episodes" && itemType == "tvshows"))
9651 std::set<CStdString> playlists;
9652 filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9654 if (xsp.GetLimit() > 0)
9655 sorting.limitEnd = xsp.GetLimit();
9656 if (xsp.GetOrder() != SortByNone)
9657 sorting.sortBy = xsp.GetOrder();
9658 if (xsp.GetOrderDirection() != SortOrderNone)
9659 sorting.sortOrder = xsp.GetOrderDirection();
9660 if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
9661 sorting.sortAttributes = SortAttributeIgnoreArticle;
9665 option = options.find("filter");
9666 if (option != options.end())
9668 CSmartPlaylist xspFilter;
9669 if (!xspFilter.LoadFromJson(option->second.asString()))
9672 // check if the filter playlist matches the item type
9673 if (xspFilter.GetType() == itemType)
9675 std::set<CStdString> playlists;
9676 filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9678 // remove the filter if it doesn't match the item type
9680 videoUrl.RemoveOption("filter");