2 * Copyright (C) 2005-2012 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 "threads/SystemClock.h"
22 #include "VideoDatabase.h"
23 #include "video/windows/GUIWindowVideoBase.h"
24 #include "utils/RegExp.h"
25 #include "addons/AddonManager.h"
26 #include "GUIInfoManager.h"
28 #include "utils/URIUtils.h"
29 #include "utils/XMLUtils.h"
30 #include "GUIPassword.h"
31 #include "filesystem/StackDirectory.h"
32 #include "filesystem/MultiPathDirectory.h"
33 #include "VideoInfoScanner.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "filesystem/Directory.h"
36 #include "filesystem/File.h"
37 #include "filesystem/SpecialProtocol.h"
38 #include "dialogs/GUIDialogExtendedProgressBar.h"
39 #include "dialogs/GUIDialogProgress.h"
40 #include "dialogs/GUIDialogYesNo.h"
42 #include "settings/AdvancedSettings.h"
43 #include "settings/GUISettings.h"
44 #include "settings/Settings.h"
45 #include "utils/StringUtils.h"
46 #include "guilib/LocalizeStrings.h"
47 #include "utils/TimeUtils.h"
48 #include "utils/log.h"
49 #include "TextureCache.h"
50 #include "addons/AddonInstaller.h"
51 #include "interfaces/AnnouncementManager.h"
52 #include "dbwrappers/dataset.h"
53 #include "utils/LabelFormatter.h"
54 #include "XBDateTime.h"
56 #include "video/VideoDbUrl.h"
57 #include "playlists/SmartPlayList.h"
58 #include "utils/GroupUtils.h"
61 using namespace dbiplus;
62 using namespace XFILE;
63 using namespace VIDEO;
64 using namespace ADDON;
66 //********************************************************************************************************************************
67 CVideoDatabase::CVideoDatabase(void)
71 //********************************************************************************************************************************
72 CVideoDatabase::~CVideoDatabase(void)
75 //********************************************************************************************************************************
76 bool CVideoDatabase::Open()
78 return CDatabase::Open(g_advancedSettings.m_databaseVideo);
81 bool CVideoDatabase::CreateTables()
83 /* indexes should be added on any columns that are used in in */
84 /* a where or a join. primary key on a column is the same as a */
85 /* unique index on that column, so there is no need to add any */
86 /* index if no other columns are refered */
88 /* order of indexes are important, for an index to be considered all */
89 /* columns up to the column in question have to have been specified */
90 /* select * from actorlinkmovie where idMovie = 1, can not take */
91 /* advantage of a index that has been created on ( idGenre, idMovie ) */
92 /*, hower on on ( idMovie, idGenre ) will be considered for use */
97 CDatabase::CreateTables();
99 CLog::Log(LOGINFO, "create bookmark table");
100 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");
101 m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
103 CLog::Log(LOGINFO, "create settings table");
104 m_pDS->exec("CREATE TABLE settings ( idFile integer, Deinterlace bool,"
105 "ViewMode integer,ZoomAmount float, PixelRatio float, VerticalShift float, AudioStream integer, SubtitleStream integer,"
106 "SubtitleDelay float, SubtitlesOn bool, Brightness float, Contrast float, Gamma float,"
107 "VolumeAmplification float, AudioDelay float, OutputToAllSpeakers bool, ResumeTime integer, Crop bool, CropLeft integer,"
108 "CropRight integer, CropTop integer, CropBottom integer, Sharpness float, NoiseReduction float, NonLinStretch bool, PostProcess bool,"
109 "ScalingMethod integer, DeinterlaceMode integer)\n");
110 m_pDS->exec("CREATE UNIQUE INDEX ix_settings ON settings ( idFile )\n");
112 CLog::Log(LOGINFO, "create stacktimes table");
113 m_pDS->exec("CREATE TABLE stacktimes (idFile integer, times text)\n");
114 m_pDS->exec("CREATE UNIQUE INDEX ix_stacktimes ON stacktimes ( idFile )\n");
116 CLog::Log(LOGINFO, "create genre table");
117 m_pDS->exec("CREATE TABLE genre ( idGenre integer primary key, strGenre text)\n");
119 CLog::Log(LOGINFO, "create genrelinkmovie table");
120 m_pDS->exec("CREATE TABLE genrelinkmovie ( idGenre integer, idMovie integer)\n");
121 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_1 ON genrelinkmovie ( idGenre, idMovie)\n");
122 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_2 ON genrelinkmovie ( idMovie, idGenre)\n");
124 CLog::Log(LOGINFO, "create country table");
125 m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
127 CLog::Log(LOGINFO, "create countrylinkmovie table");
128 m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
129 m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
130 m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
132 CLog::Log(LOGINFO, "create movie table");
133 CStdString columns = "CREATE TABLE movie ( idMovie integer primary key, idFile integer";
134 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
137 column.Format(",c%02d text", i);
140 columns += ", idSet integer)";
141 m_pDS->exec(columns.c_str());
142 m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
143 m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
145 CLog::Log(LOGINFO, "create actorlinkmovie table");
146 m_pDS->exec("CREATE TABLE actorlinkmovie ( idActor integer, idMovie integer, strRole text, iOrder integer)\n");
147 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_1 ON actorlinkmovie ( idActor, idMovie )\n");
148 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_2 ON actorlinkmovie ( idMovie, idActor )\n");
150 CLog::Log(LOGINFO, "create directorlinkmovie table");
151 m_pDS->exec("CREATE TABLE directorlinkmovie ( idDirector integer, idMovie integer)\n");
152 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_1 ON directorlinkmovie ( idDirector, idMovie )\n");
153 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_2 ON directorlinkmovie ( idMovie, idDirector )\n");
155 CLog::Log(LOGINFO, "create writerlinkmovie table");
156 m_pDS->exec("CREATE TABLE writerlinkmovie ( idWriter integer, idMovie integer)\n");
157 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_1 ON writerlinkmovie ( idWriter, idMovie )\n");
158 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_2 ON writerlinkmovie ( idMovie, idWriter )\n");
160 CLog::Log(LOGINFO, "create actors table");
161 m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
163 CLog::Log(LOGINFO, "create path table");
164 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)");
165 m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
167 CLog::Log(LOGINFO, "create files table");
168 m_pDS->exec("CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text, dateAdded text)");
169 m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
171 CLog::Log(LOGINFO, "create tvshow table");
172 columns = "CREATE TABLE tvshow ( idShow integer primary key";
173 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
176 column.Format(",c%02d text", i);
180 m_pDS->exec(columns.c_str());
182 CLog::Log(LOGINFO, "create directorlinktvshow table");
183 m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
184 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
185 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
187 CLog::Log(LOGINFO, "create actorlinktvshow table");
188 m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text, iOrder integer)\n");
189 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
190 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
192 CLog::Log(LOGINFO, "create studiolinktvshow table");
193 m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
194 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
195 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
197 CLog::Log(LOGINFO, "create episode table");
198 columns = "CREATE TABLE episode ( idEpisode integer primary key, idFile integer";
199 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
202 if ( i == VIDEODB_ID_EPISODE_SEASON || i == VIDEODB_ID_EPISODE_EPISODE || i == VIDEODB_ID_EPISODE_BOOKMARK)
203 column.Format(",c%02d varchar(24)", i);
205 column.Format(",c%02d text", i);
209 columns += ", idShow integer)";
210 m_pDS->exec(columns.c_str());
211 m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
212 m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
213 CStdString createColIndex;
214 createColIndex.Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
215 m_pDS->exec(createColIndex.c_str());
216 createColIndex.Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
217 m_pDS->exec(createColIndex.c_str());
218 m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
219 m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
221 CLog::Log(LOGINFO, "create tvshowlinkpath table");
222 m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
223 m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
224 m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
226 CLog::Log(LOGINFO, "create actorlinkepisode table");
227 m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text, iOrder integer)\n");
228 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
229 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
231 CLog::Log(LOGINFO, "create directorlinkepisode table");
232 m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
233 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
234 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
236 CLog::Log(LOGINFO, "create writerlinkepisode table");
237 m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
238 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
239 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
241 CLog::Log(LOGINFO, "create genrelinktvshow table");
242 m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
243 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
244 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
246 CLog::Log(LOGINFO, "create movielinktvshow table");
247 m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
248 m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
249 m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
251 CLog::Log(LOGINFO, "create studio table");
252 m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
254 CLog::Log(LOGINFO, "create studiolinkmovie table");
255 m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
256 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
257 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
259 CLog::Log(LOGINFO, "create musicvideo table");
260 columns = "CREATE TABLE musicvideo ( idMVideo integer primary key, idFile integer";
261 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
264 column.Format(",c%02d text", i);
268 m_pDS->exec(columns.c_str());
270 m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
271 m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
273 CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
274 m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
275 m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
276 m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
278 CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
279 m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
280 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
281 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
283 CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
284 m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
285 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
286 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
288 CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
289 m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
290 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
291 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
293 CLog::Log(LOGINFO, "create streaminfo table");
294 m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
295 "strVideoCodec text, fVideoAspect float, iVideoWidth integer, iVideoHeight integer, "
296 "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text, iVideoDuration integer)");
297 m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
299 CLog::Log(LOGINFO, "create sets table");
300 m_pDS->exec("CREATE TABLE sets ( idSet integer primary key, strSet text)\n");
302 // create basepath indices
303 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
304 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
305 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
306 m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c17(12) )");
308 CLog::Log(LOGINFO, "create seasons table");
309 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
310 m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
312 CLog::Log(LOGINFO, "create art table");
313 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
314 m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
316 CLog::Log(LOGINFO, "create tag table");
317 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
318 m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
320 CLog::Log(LOGINFO, "create taglinks table");
321 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
322 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
323 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
324 m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
326 CLog::Log(LOGINFO, "create deletion triggers");
327 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
328 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
329 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
331 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
332 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
333 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
335 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
336 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
337 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
339 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
340 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
342 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
343 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
345 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
346 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
348 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
349 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
351 m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
352 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
355 // we create views last to ensure all indexes are rolled in
360 CLog::Log(LOGERROR, "%s unable to create tables:%i", __FUNCTION__, (int)GetLastError());
361 RollbackTransaction();
368 void CVideoDatabase::CreateViews()
370 CLog::Log(LOGINFO, "create episodeview");
371 m_pDS->exec("DROP VIEW IF EXISTS episodeview");
372 CStdString episodeview = PrepareSQL("CREATE VIEW episodeview AS SELECT "
374 " files.strFileName AS strFileName,"
375 " path.strPath AS strPath,"
376 " files.playCount AS playCount,"
377 " files.lastPlayed AS lastPlayed,"
378 " files.dateAdded AS dateAdded,"
379 " tvshow.c%02d AS strTitle,"
380 " tvshow.c%02d AS strStudio,"
381 " tvshow.c%02d AS premiered,"
382 " tvshow.c%02d AS mpaa,"
383 " tvshow.c%02d AS strShowPath, "
384 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
385 " bookmark.totalTimeInSeconds AS totalTimeInSeconds, "
386 " seasons.idSeason AS idSeason "
389 " files.idFile=episode.idFile"
391 " tvshow.idShow=episode.idShow"
392 " LEFT JOIN seasons ON"
393 " seasons.idShow=episode.idShow AND seasons.season=episode.c%02d"
395 " files.idPath=path.idPath"
396 " LEFT JOIN bookmark ON"
397 " 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);
398 m_pDS->exec(episodeview.c_str());
400 CLog::Log(LOGINFO, "create tvshowview");
401 m_pDS->exec("DROP VIEW IF EXISTS tvshowview");
402 CStdString tvshowview = PrepareSQL("CREATE VIEW tvshowview AS SELECT "
404 " path.strPath AS strPath,"
405 " path.dateAdded AS dateAdded,"
406 " MAX(files.lastPlayed) AS lastPlayed,"
407 " NULLIF(COUNT(episode.c12), 0) AS totalCount,"
408 " COUNT(files.playCount) AS watchedcount,"
409 " NULLIF(COUNT(DISTINCT(episode.c12)), 0) AS totalSeasons "
411 " LEFT JOIN tvshowlinkpath ON"
412 " tvshowlinkpath.idShow=tvshow.idShow"
414 " path.idPath=tvshowlinkpath.idPath"
415 " LEFT JOIN episode ON"
416 " episode.idShow=tvshow.idShow"
417 " LEFT JOIN files ON"
418 " files.idFile=episode.idFile "
419 "GROUP BY tvshow.idShow;");
420 m_pDS->exec(tvshowview.c_str());
422 CLog::Log(LOGINFO, "create musicvideoview");
423 m_pDS->exec("DROP VIEW IF EXISTS musicvideoview");
424 m_pDS->exec("CREATE VIEW musicvideoview AS SELECT"
426 " files.strFileName as strFileName,"
427 " path.strPath as strPath,"
428 " files.playCount as playCount,"
429 " files.lastPlayed as lastPlayed,"
430 " files.dateAdded as dateAdded, "
431 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
432 " bookmark.totalTimeInSeconds AS totalTimeInSeconds "
435 " files.idFile=musicvideo.idFile"
437 " path.idPath=files.idPath"
438 " LEFT JOIN bookmark ON"
439 " bookmark.idFile=musicvideo.idFile AND bookmark.type=1");
441 CLog::Log(LOGINFO, "create movieview");
442 m_pDS->exec("DROP VIEW IF EXISTS movieview");
443 m_pDS->exec("CREATE VIEW movieview AS SELECT"
445 " sets.strSet AS strSet,"
446 " files.strFileName AS strFileName,"
447 " path.strPath AS strPath,"
448 " files.playCount AS playCount,"
449 " files.lastPlayed AS lastPlayed, "
450 " files.dateAdded AS dateAdded, "
451 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
452 " bookmark.totalTimeInSeconds AS totalTimeInSeconds "
455 " sets.idSet = movie.idSet"
457 " files.idFile=movie.idFile"
459 " path.idPath=files.idPath"
460 " LEFT JOIN bookmark ON"
461 " bookmark.idFile=movie.idFile AND bookmark.type=1");
464 //********************************************************************************************************************************
465 int CVideoDatabase::GetPathId(const CStdString& strPath)
471 if (NULL == m_pDB.get()) return -1;
472 if (NULL == m_pDS.get()) return -1;
474 CStdString strPath1(strPath);
475 if (URIUtils::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
476 URIUtils::GetParentPath(strPath,strPath1);
478 URIUtils::AddSlashAtEnd(strPath1);
480 strSQL=PrepareSQL("select idPath from path where strPath='%s'",strPath1.c_str());
481 m_pDS->query(strSQL.c_str());
483 idPath = m_pDS->fv("path.idPath").get_asInt();
490 CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
495 bool CVideoDatabase::GetPaths(set<CStdString> &paths)
499 if (NULL == m_pDB.get()) return false;
500 if (NULL == m_pDS.get()) return false;
504 // grab all paths with movie content set
505 if (!m_pDS->query("select strPath,noUpdate from path"
506 " where (strContent = 'movies' or strContent = 'musicvideos')"
507 " and strPath NOT like 'multipath://%%'"
508 " order by strPath"))
511 while (!m_pDS->eof())
513 if (!m_pDS->fv("noUpdate").get_asBool())
514 paths.insert(m_pDS->fv("strPath").get_asString());
519 // then grab all tvshow paths
520 if (!m_pDS->query("select strPath,noUpdate from path"
521 " where ( strContent = 'tvshows'"
522 " or idPath in (select idPath from tvshowlinkpath))"
523 " and strPath NOT like 'multipath://%%'"
524 " order by strPath"))
527 while (!m_pDS->eof())
529 if (!m_pDS->fv("noUpdate").get_asBool())
530 paths.insert(m_pDS->fv("strPath").get_asString());
535 // finally grab all other paths holding a movie which is not a stack or a rar archive
536 // - this isnt perfect but it should do fine in most situations.
537 // reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
538 // not making mistakes must take priority
539 if (!m_pDS->query("select strPath,noUpdate from path"
540 " where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
541 " and idPath NOT in (select idPath from tvshowlinkpath)"
542 " 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
543 " and idPath NOT in (select idPath from files where strFileName like 'index.bdmv')" // bluray folders get stacked to a single item in parent folder
544 " and strPath NOT like 'multipath://%%'"
545 " and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
546 " order by strPath"))
549 while (!m_pDS->eof())
551 if (!m_pDS->fv("noUpdate").get_asBool())
552 paths.insert(m_pDS->fv("strPath").get_asString());
560 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
565 bool CVideoDatabase::GetPathsForTvShow(int idShow, set<int>& paths)
570 if (NULL == m_pDB.get()) return false;
571 if (NULL == m_pDS.get()) return false;
572 strSQL = PrepareSQL("SELECT DISTINCT idPath FROM files JOIN episode ON episode.idFile=files.idFile WHERE episode.idShow=%i",idShow);
573 m_pDS->query(strSQL.c_str());
574 while (!m_pDS->eof())
576 paths.insert(m_pDS->fv(0).get_asInt());
584 CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
589 int CVideoDatabase::RunQuery(const CStdString &sql)
591 unsigned int time = XbmcThreads::SystemClockMillis();
593 if (m_pDS->query(sql.c_str()))
595 rows = m_pDS->num_rows();
599 CLog::Log(LOGDEBUG, "%s took %d ms for %d items query: %s", __FUNCTION__, XbmcThreads::SystemClockMillis() - time, rows, sql.c_str());
603 bool CVideoDatabase::GetSubPaths(const CStdString &basepath, vector< pair<int,string> >& subpaths)
608 if (!m_pDB.get() || !m_pDS.get())
611 CStdString path(basepath);
612 URIUtils::AddSlashAtEnd(path);
613 sql = PrepareSQL("SELECT idPath,strPath FROM path WHERE SUBSTR(strPath,1,%i)='%s'", StringUtils::utf8_strlen(path.c_str()), path.c_str());
614 m_pDS->query(sql.c_str());
615 while (!m_pDS->eof())
617 subpaths.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
625 CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, sql.c_str());
630 int CVideoDatabase::AddPath(const CStdString& strPath, const CStdString &strDateAdded /*= "" */)
635 int idPath = GetPathId(strPath);
637 return idPath; // already have the path
639 if (NULL == m_pDB.get()) return -1;
640 if (NULL == m_pDS.get()) return -1;
642 CStdString strPath1(strPath);
643 if (URIUtils::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
644 URIUtils::GetParentPath(strPath,strPath1);
646 URIUtils::AddSlashAtEnd(strPath1);
648 // only set dateadded if we got one
649 if (!strDateAdded.empty())
650 strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper, dateAdded) values (NULL,'%s','','', '%s')", strPath1.c_str(), strDateAdded.c_str());
652 strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
653 m_pDS->exec(strSQL.c_str());
654 idPath = (int)m_pDS->lastinsertid();
659 CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
664 bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
668 if (NULL == m_pDB.get()) return false;
669 if (NULL == m_pDS.get()) return false;
671 CStdString strSQL=PrepareSQL("select strHash from path where strPath='%s'", path.c_str());
672 m_pDS->query(strSQL.c_str());
673 if (m_pDS->num_rows() == 0)
675 hash = m_pDS->fv("strHash").get_asString();
680 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
686 //********************************************************************************************************************************
687 int CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
689 CStdString strSQL = "";
693 if (NULL == m_pDB.get()) return -1;
694 if (NULL == m_pDS.get()) return -1;
696 CStdString strFileName, strPath;
697 SplitPath(strFileNameAndPath,strPath,strFileName);
699 int idPath = AddPath(strPath);
703 CStdString strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
705 m_pDS->query(strSQL.c_str());
706 if (m_pDS->num_rows() > 0)
708 idFile = m_pDS->fv("idFile").get_asInt() ;
714 strSQL=PrepareSQL("insert into files (idFile, idPath, strFileName) values(NULL, %i, '%s')", idPath, strFileName.c_str());
715 m_pDS->exec(strSQL.c_str());
716 idFile = (int)m_pDS->lastinsertid();
721 CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
726 int CVideoDatabase::AddFile(const CFileItem& item)
728 if (item.IsVideoDb() && item.HasVideoInfoTag())
729 return AddFile(item.GetVideoInfoTag()->m_strFileNameAndPath);
730 return AddFile(item.GetPath());
733 void CVideoDatabase::UpdateFileDateAdded(int idFile, const CStdString& strFileNameAndPath)
735 if (idFile < 0 || strFileNameAndPath.empty())
738 CStdString strSQL = "";
741 if (NULL == m_pDB.get()) return;
742 if (NULL == m_pDS.get()) return;
744 CStdString file = strFileNameAndPath;
745 if (URIUtils::IsStack(strFileNameAndPath))
746 file = CStackDirectory::GetFirstStackedFile(strFileNameAndPath);
748 if (URIUtils::IsInArchive(file))
749 file = CURL(file).GetHostName();
752 // Skip looking at the files ctime/mtime if defined by the user through as.xml
753 if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
755 // Let's try to get the modification datetime
756 struct __stat64 buffer;
757 if (CFile::Stat(file, &buffer) == 0 && (buffer.st_mtime != 0 || buffer.st_ctime !=0))
759 time_t now = time(NULL);
761 // Prefer the modification time if it's valid
762 if (g_advancedSettings.m_iVideoLibraryDateAdded == 1)
764 if (buffer.st_mtime != 0 && (time_t)buffer.st_mtime <= now)
765 addedTime = (time_t)buffer.st_mtime;
767 addedTime = (time_t)buffer.st_ctime;
769 // Use the newer of the creation and modification time
772 addedTime = max((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
773 // if the newer of the two dates is in the future, we try it with the older one
775 addedTime = min((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
778 // make sure the datetime does is not in the future
779 if (addedTime <= now)
781 struct tm *time = localtime(&addedTime);
788 if (!dateAdded.IsValid())
789 dateAdded = CDateTime::GetCurrentDateTime();
791 strSQL = PrepareSQL("update files set dateAdded='%s' where idFile=%d", dateAdded.GetAsDBDateTime().c_str(), idFile);
792 m_pDS->exec(strSQL.c_str());
796 CLog::Log(LOGERROR, "%s unable to update dateadded for file (%s)", __FUNCTION__, strSQL.c_str());
800 bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
804 if (NULL == m_pDB.get()) return false;
805 if (NULL == m_pDS.get()) return false;
808 { // this is an empty folder - we need only add it to the path table
809 // if the path actually exists
810 if (!CDirectory::Exists(path))
813 int idPath = AddPath(path);
814 if (idPath < 0) return false;
816 CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
817 m_pDS->exec(strSQL.c_str());
823 CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
829 bool CVideoDatabase::LinkMovieToTvshow(int idMovie, int idShow, bool bRemove)
833 if (NULL == m_pDB.get()) return false;
834 if (NULL == m_pDS.get()) return false;
836 if (bRemove) // delete link
838 CStdString strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i and idShow=%i", idMovie, idShow);
839 m_pDS->exec(strSQL.c_str());
843 CStdString strSQL=PrepareSQL("insert into movielinktvshow (idShow,idMovie) values (%i,%i)", idShow,idMovie);
844 m_pDS->exec(strSQL.c_str());
850 CLog::Log(LOGERROR, "%s (%i, %i) failed", __FUNCTION__, idMovie, idShow);
856 bool CVideoDatabase::IsLinkedToTvshow(int idMovie)
860 if (NULL == m_pDB.get()) return false;
861 if (NULL == m_pDS.get()) return false;
863 CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
864 m_pDS->query(strSQL.c_str());
876 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
882 bool CVideoDatabase::GetLinksToTvShow(int idMovie, vector<int>& ids)
886 if (NULL == m_pDB.get()) return false;
887 if (NULL == m_pDS.get()) return false;
889 CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
890 m_pDS2->query(strSQL.c_str());
891 while (!m_pDS2->eof())
893 ids.push_back(m_pDS2->fv(1).get_asInt());
902 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
909 //********************************************************************************************************************************
910 int CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
914 if (NULL == m_pDB.get()) return -1;
915 if (NULL == m_pDS.get()) return -1;
916 CStdString strPath, strFileName;
917 SplitPath(strFilenameAndPath,strPath,strFileName);
919 int idPath = GetPathId(strPath);
923 strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
924 m_pDS->query(strSQL.c_str());
925 if (m_pDS->num_rows() > 0)
927 int idFile = m_pDS->fv("files.idFile").get_asInt();
935 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
940 int CVideoDatabase::GetFileId(const CFileItem &item)
942 if (item.IsVideoDb() && item.HasVideoInfoTag())
943 return GetFileId(item.GetVideoInfoTag()->m_strFileNameAndPath);
944 return GetFileId(item.GetPath());
947 //********************************************************************************************************************************
948 int CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
952 if (NULL == m_pDB.get()) return -1;
953 if (NULL == m_pDS.get()) return -1;
956 // needed for query parameters
957 int idFile = GetFileId(strFilenameAndPath);
963 SplitPath(strFilenameAndPath,strPath,strFile);
965 // have to join movieinfo table for correct results
966 idPath = GetPathId(strPath);
967 if (idPath < 0 && strPath != strFilenameAndPath)
971 if (idFile == -1 && strPath != strFilenameAndPath)
976 strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
978 strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
980 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
981 m_pDS->query(strSQL.c_str());
982 if (m_pDS->num_rows() > 0)
983 idMovie = m_pDS->fv("idMovie").get_asInt();
990 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
995 int CVideoDatabase::GetTvShowId(const CStdString& strPath)
999 if (NULL == m_pDB.get()) return -1;
1000 if (NULL == m_pDS.get()) return -1;
1003 // have to join movieinfo table for correct results
1004 int idPath = GetPathId(strPath);
1009 CStdString strPath1=strPath;
1010 CStdString strParent;
1013 strSQL=PrepareSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%i",idPath);
1014 m_pDS->query(strSQL);
1018 while (iFound == 0 && URIUtils::GetParentPath(strPath1, strParent))
1020 strSQL=PrepareSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and strPath='%s'",strParent.c_str());
1021 m_pDS->query(strSQL.c_str());
1024 int idShow = m_pDS->fv("idShow").get_asInt();
1028 strPath1 = strParent;
1031 if (m_pDS->num_rows() > 0)
1032 idTvShow = m_pDS->fv("idShow").get_asInt();
1039 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1044 int CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, int idEpisode, int idSeason) // input value is episode/season number hint - for multiparters
1048 if (NULL == m_pDB.get()) return -1;
1049 if (NULL == m_pDS.get()) return -1;
1051 // need this due to the nested GetEpisodeInfo query
1052 auto_ptr<Dataset> pDS;
1053 pDS.reset(m_pDB->CreateDataset());
1054 if (NULL == pDS.get()) return -1;
1056 int idFile = GetFileId(strFilenameAndPath);
1060 CStdString strSQL=PrepareSQL("select idEpisode from episode where idFile=%i", idFile);
1062 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
1063 pDS->query(strSQL.c_str());
1064 if (pDS->num_rows() > 0)
1066 if (idEpisode == -1)
1067 idEpisode = pDS->fv("episode.idEpisode").get_asInt();
1068 else // use the hint!
1073 int idTmpEpisode = pDS->fv("episode.idEpisode").get_asInt();
1074 GetEpisodeInfo(strFilenameAndPath,tag,idTmpEpisode);
1075 if (tag.m_iEpisode == idEpisode && (idSeason == -1 || tag.m_iSeason == idSeason)) {
1076 // match on the episode hint, and there's no season hint or a season hint match
1077 idEpisode = idTmpEpisode;
1095 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1100 int CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
1104 if (NULL == m_pDB.get()) return -1;
1105 if (NULL == m_pDS.get()) return -1;
1107 int idFile = GetFileId(strFilenameAndPath);
1111 CStdString strSQL=PrepareSQL("select idMVideo from musicvideo where idFile=%i", idFile);
1113 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
1114 m_pDS->query(strSQL.c_str());
1116 if (m_pDS->num_rows() > 0)
1117 idMVideo = m_pDS->fv("idMVideo").get_asInt();
1124 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1129 //********************************************************************************************************************************
1130 int CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
1134 if (NULL == m_pDB.get()) return -1;
1135 if (NULL == m_pDS.get()) return -1;
1137 int idMovie = GetMovieId(strFilenameAndPath);
1140 int idFile = AddFile(strFilenameAndPath);
1143 UpdateFileDateAdded(idFile, strFilenameAndPath);
1144 CStdString strSQL=PrepareSQL("insert into movie (idMovie, idFile) values (NULL, %i)", idFile);
1145 m_pDS->exec(strSQL.c_str());
1146 idMovie = (int)m_pDS->lastinsertid();
1147 // CommitTransaction();
1154 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1159 int CVideoDatabase::AddTvShow(const CStdString& strPath)
1163 if (NULL == m_pDB.get()) return -1;
1164 if (NULL == m_pDS.get()) return -1;
1166 CStdString strSQL=PrepareSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath='%s' and path.idPath=tvshowlinkpath.idPath",strPath.c_str());
1167 m_pDS->query(strSQL.c_str());
1168 if (m_pDS->num_rows() != 0)
1169 return m_pDS->fv("tvshowlinkpath.idShow").get_asInt();
1171 strSQL=PrepareSQL("insert into tvshow (idShow) values (NULL)");
1172 m_pDS->exec(strSQL.c_str());
1173 int idTvShow = (int)m_pDS->lastinsertid();
1175 // Get the creation datetime of the tvshow directory
1176 CDateTime dateAdded;
1177 // Skip looking at the files ctime/mtime if defined by the user through as.xml
1178 if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
1180 struct __stat64 buffer;
1181 if (XFILE::CFile::Stat(strPath, &buffer) == 0)
1183 time_t now = time(NULL);
1184 // Make sure we have a valid date (i.e. not in the future)
1185 if ((time_t)buffer.st_ctime <= now)
1187 struct tm *time = localtime((const time_t*)&buffer.st_ctime);
1194 if (!dateAdded.IsValid())
1195 dateAdded = CDateTime::GetCurrentDateTime();
1197 int idPath = AddPath(strPath, dateAdded.GetAsDBDateTime());
1198 strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
1199 m_pDS->exec(strSQL.c_str());
1201 // CommitTransaction();
1207 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1212 //********************************************************************************************************************************
1213 int CVideoDatabase::AddEpisode(int idShow, const CStdString& strFilenameAndPath)
1217 if (NULL == m_pDB.get()) return -1;
1218 if (NULL == m_pDS.get()) return -1;
1220 int idFile = AddFile(strFilenameAndPath);
1223 UpdateFileDateAdded(idFile, strFilenameAndPath);
1225 CStdString strSQL=PrepareSQL("insert into episode (idEpisode, idFile, idShow) values (NULL, %i, %i)", idFile, idShow);
1226 m_pDS->exec(strSQL.c_str());
1227 return (int)m_pDS->lastinsertid();
1231 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1236 int CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
1240 if (NULL == m_pDB.get()) return -1;
1241 if (NULL == m_pDS.get()) return -1;
1243 int idMVideo = GetMusicVideoId(strFilenameAndPath);
1246 int idFile = AddFile(strFilenameAndPath);
1249 UpdateFileDateAdded(idFile, strFilenameAndPath);
1250 CStdString strSQL=PrepareSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %i)", idFile);
1251 m_pDS->exec(strSQL.c_str());
1252 idMVideo = (int)m_pDS->lastinsertid();
1259 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1264 //********************************************************************************************************************************
1265 int CVideoDatabase::AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value)
1269 if (NULL == m_pDB.get()) return -1;
1270 if (NULL == m_pDS.get()) return -1;
1272 CStdString strSQL = PrepareSQL("select %s from %s where %s like '%s'", firstField.c_str(), table.c_str(), secondField.c_str(), value.c_str());
1273 m_pDS->query(strSQL.c_str());
1274 if (m_pDS->num_rows() == 0)
1277 // doesnt exists, add it
1278 strSQL = PrepareSQL("insert into %s (%s, %s) values(NULL, '%s')", table.c_str(), firstField.c_str(), secondField.c_str(), value.c_str());
1279 m_pDS->exec(strSQL.c_str());
1280 int id = (int)m_pDS->lastinsertid();
1285 int id = m_pDS->fv(firstField).get_asInt();
1292 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, value.c_str() );
1298 int CVideoDatabase::AddSet(const CStdString& strSet)
1303 return AddToTable("sets", "idSet", "strSet", strSet);
1306 int CVideoDatabase::AddTag(const std::string& tag)
1311 return AddToTable("tag", "idTag", "strTag", tag);
1314 int CVideoDatabase::AddGenre(const CStdString& strGenre)
1316 return AddToTable("genre", "idGenre", "strGenre", strGenre);
1319 int CVideoDatabase::AddStudio(const CStdString& strStudio)
1321 return AddToTable("studio", "idStudio", "strStudio", strStudio);
1324 //********************************************************************************************************************************
1325 int CVideoDatabase::AddCountry(const CStdString& strCountry)
1327 return AddToTable("country", "idCountry", "strCountry", strCountry);
1330 int CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& thumbURLs, const CStdString &thumb)
1334 if (NULL == m_pDB.get()) return -1;
1335 if (NULL == m_pDS.get()) return -1;
1337 CStdString strSQL=PrepareSQL("select idActor from actors where strActor like '%s'", strActor.c_str());
1338 m_pDS->query(strSQL.c_str());
1339 if (m_pDS->num_rows() == 0)
1342 // doesnt exists, add it
1343 strSQL=PrepareSQL("insert into actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),thumbURLs.c_str());
1344 m_pDS->exec(strSQL.c_str());
1345 idActor = (int)m_pDS->lastinsertid();
1349 idActor = m_pDS->fv("idActor").get_asInt();
1351 // update the thumb url's
1352 if (!thumbURLs.IsEmpty())
1354 strSQL=PrepareSQL("update actors set strThumb='%s' where idActor=%i",thumbURLs.c_str(),idActor);
1355 m_pDS->exec(strSQL.c_str());
1359 if (!thumb.IsEmpty())
1360 SetArtForItem(idActor, "actor", "thumb", thumb);
1365 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
1372 void CVideoDatabase::AddLinkToActor(const char *table, int actorID, const char *secondField, int secondID, const CStdString &role, int order)
1376 if (NULL == m_pDB.get()) return ;
1377 if (NULL == m_pDS.get()) return ;
1379 CStdString strSQL=PrepareSQL("select * from %s where idActor=%i and %s=%i", table, actorID, secondField, secondID);
1380 m_pDS->query(strSQL.c_str());
1381 if (m_pDS->num_rows() == 0)
1383 // doesnt exists, add it
1384 strSQL=PrepareSQL("insert into %s (idActor, %s, strRole, iOrder) values(%i,%i,'%s',%i)", table, secondField, actorID, secondID, role.c_str(), order);
1385 m_pDS->exec(strSQL.c_str());
1391 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1395 void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1399 if (NULL == m_pDB.get()) return ;
1400 if (NULL == m_pDS.get()) return ;
1402 CStdString strSQL = PrepareSQL("select * from %s where %s=%i and %s=%i", table, firstField, firstID, secondField, secondID);
1403 if (typeField != NULL && type != NULL)
1404 strSQL += PrepareSQL(" and %s='%s'", typeField, type);
1405 m_pDS->query(strSQL.c_str());
1406 if (m_pDS->num_rows() == 0)
1408 // doesnt exists, add it
1409 if (typeField == NULL || type == NULL)
1410 strSQL = PrepareSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
1412 strSQL = PrepareSQL("insert into %s (%s,%s,%s) values(%i,%i,'%s')", table, firstField, secondField, typeField, firstID, secondID, type);
1413 m_pDS->exec(strSQL.c_str());
1419 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1423 void CVideoDatabase::RemoveFromLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1427 if (NULL == m_pDB.get()) return ;
1428 if (NULL == m_pDS.get()) return ;
1430 CStdString strSQL = PrepareSQL("DELETE FROM %s WHERE %s = %i AND %s = %i", table, firstField, firstID, secondField, secondID);
1431 if (typeField != NULL && type != NULL)
1432 strSQL += PrepareSQL(" AND %s='%s'", typeField, type);
1433 m_pDS->exec(strSQL.c_str());
1437 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1442 void CVideoDatabase::AddTagToItem(int idMovie, int idTag, const std::string &type)
1447 AddToLinkTable("taglinks", "idTag", idTag, "idMedia", idMovie, "media_type", type.c_str());
1450 void CVideoDatabase::RemoveTagFromItem(int idItem, int idTag, const std::string &type)
1455 RemoveFromLinkTable("taglinks", "idTag", idTag, "idMedia", idItem, "media_type", type.c_str());
1458 void CVideoDatabase::RemoveTagsFromItem(int idItem, const std::string &type)
1463 m_pDS2->exec(PrepareSQL("DELETE FROM taglinks WHERE idMedia=%d AND media_type='%s'", idItem, type.c_str()));
1467 void CVideoDatabase::AddActorToMovie(int idMovie, int idActor, const CStdString& strRole, int order)
1469 AddLinkToActor("actorlinkmovie", idActor, "idMovie", idMovie, strRole, order);
1472 void CVideoDatabase::AddActorToTvShow(int idTvShow, int idActor, const CStdString& strRole, int order)
1474 AddLinkToActor("actorlinktvshow", idActor, "idShow", idTvShow, strRole, order);
1477 void CVideoDatabase::AddActorToEpisode(int idEpisode, int idActor, const CStdString& strRole, int order)
1479 AddLinkToActor("actorlinkepisode", idActor, "idEpisode", idEpisode, strRole, order);
1482 void CVideoDatabase::AddArtistToMusicVideo(int idMVideo, int idArtist)
1484 AddToLinkTable("artistlinkmusicvideo", "idArtist", idArtist, "idMVideo", idMVideo);
1487 //****Directors + Writers****
1488 void CVideoDatabase::AddDirectorToMovie(int idMovie, int idDirector)
1490 AddToLinkTable("directorlinkmovie", "idDirector", idDirector, "idMovie", idMovie);
1493 void CVideoDatabase::AddDirectorToTvShow(int idTvShow, int idDirector)
1495 AddToLinkTable("directorlinktvshow", "idDirector", idDirector, "idShow", idTvShow);
1498 void CVideoDatabase::AddWriterToEpisode(int idEpisode, int idWriter)
1500 AddToLinkTable("writerlinkepisode", "idWriter", idWriter, "idEpisode", idEpisode);
1503 void CVideoDatabase::AddWriterToMovie(int idMovie, int idWriter)
1505 AddToLinkTable("writerlinkmovie", "idWriter", idWriter, "idMovie", idMovie);
1508 void CVideoDatabase::AddDirectorToEpisode(int idEpisode, int idDirector)
1510 AddToLinkTable("directorlinkepisode", "idDirector", idDirector, "idEpisode", idEpisode);
1513 void CVideoDatabase::AddDirectorToMusicVideo(int idMVideo, int idDirector)
1515 AddToLinkTable("directorlinkmusicvideo", "idDirector", idDirector, "idMVideo", idMVideo);
1519 void CVideoDatabase::AddStudioToMovie(int idMovie, int idStudio)
1521 AddToLinkTable("studiolinkmovie", "idStudio", idStudio, "idMovie", idMovie);
1524 void CVideoDatabase::AddStudioToTvShow(int idTvShow, int idStudio)
1526 AddToLinkTable("studiolinktvshow", "idStudio", idStudio, "idShow", idTvShow);
1529 void CVideoDatabase::AddStudioToMusicVideo(int idMVideo, int idStudio)
1531 AddToLinkTable("studiolinkmusicvideo", "idStudio", idStudio, "idMVideo", idMVideo);
1535 void CVideoDatabase::AddGenreToMovie(int idMovie, int idGenre)
1537 AddToLinkTable("genrelinkmovie", "idGenre", idGenre, "idMovie", idMovie);
1540 void CVideoDatabase::AddGenreToTvShow(int idTvShow, int idGenre)
1542 AddToLinkTable("genrelinktvshow", "idGenre", idGenre, "idShow", idTvShow);
1545 void CVideoDatabase::AddGenreToMusicVideo(int idMVideo, int idGenre)
1547 AddToLinkTable("genrelinkmusicvideo", "idGenre", idGenre, "idMVideo", idMVideo);
1551 void CVideoDatabase::AddCountryToMovie(int idMovie, int idCountry)
1553 AddToLinkTable("countrylinkmovie", "idCountry", idCountry, "idMovie", idMovie);
1556 //********************************************************************************************************************************
1557 bool CVideoDatabase::LoadVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details)
1559 if (GetMovieInfo(strFilenameAndPath, details))
1561 if (GetEpisodeInfo(strFilenameAndPath, details))
1563 if (GetMusicVideoInfo(strFilenameAndPath, details))
1565 if (GetFileInfo(strFilenameAndPath, details))
1571 bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
1575 if (NULL == m_pDB.get()) return false;
1576 if (NULL == m_pDS.get()) return false;
1577 int idMovie = GetMovieId(strFilenameAndPath);
1578 return (idMovie > 0); // index of zero is also invalid
1582 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1587 bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
1591 if (NULL == m_pDB.get()) return false;
1592 if (NULL == m_pDS.get()) return false;
1593 int idTvShow = GetTvShowId(strPath);
1594 return (idTvShow > 0); // index of zero is also invalid
1598 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1603 bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
1607 if (NULL == m_pDB.get()) return false;
1608 if (NULL == m_pDS.get()) return false;
1609 int idEpisode = GetEpisodeId(strFilenameAndPath);
1610 return (idEpisode > 0); // index of zero is also invalid
1614 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1619 bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
1623 if (NULL == m_pDB.get()) return false;
1624 if (NULL == m_pDS.get()) return false;
1625 int idMVideo = GetMusicVideoId(strFilenameAndPath);
1626 return (idMVideo > 0); // index of zero is also invalid
1630 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1635 void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath, int idTvShow /* = -1 */)
1639 if (NULL == m_pDB.get()) return ;
1640 if (NULL == m_pDS.get()) return ;
1644 idTvShow = GetTvShowId(strPath);
1650 strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
1651 m_pDS->exec(strSQL.c_str());
1653 strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
1654 m_pDS->exec(strSQL.c_str());
1656 strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
1657 m_pDS->exec(strSQL.c_str());
1659 strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
1660 m_pDS->exec(strSQL.c_str());
1662 // remove all info other than the id
1663 // we do this due to the way we have the link between the file + movie tables.
1665 strSQL = "update tvshow set ";
1666 for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1669 column.Format("c%02d=NULL,", iType);
1672 strSQL = strSQL.Mid(0, strSQL.size() - 1) + PrepareSQL(" where idShow=%i", idTvShow);
1673 m_pDS->exec(strSQL.c_str());
1677 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1681 //********************************************************************************************************************************
1682 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
1685 filter.join = "LEFT JOIN actorlinkmovie ON actorlinkmovie.idMovie=movieview.idMovie "
1686 "LEFT JOIN actors a ON a.idActor=actorlinkmovie.idActor "
1687 "LEFT JOIN directorlinkmovie ON directorlinkmovie.idMovie=movieview.idMovie "
1688 "LEFT JOIN actors d ON d.idActor=directorlinkmovie.idDirector";
1689 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1690 filter.group = "movieview.idMovie";
1691 GetMoviesByWhere("videodb://1/2/", filter, items);
1694 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
1697 filter.join = "LEFT JOIN actorlinktvshow ON actorlinktvshow.idShow=tvshowview.idShow "
1698 "LEFT JOIN actors a ON a.idActor=actorlinktvshow.idActor "
1699 "LEFT JOIN directorlinktvshow ON directorlinktvshow.idShow=tvshowview.idShow "
1700 "LEFT JOIN actors d ON d.idActor=directorlinktvshow.idDirector";
1701 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1702 filter.group = "tvshowview.idShow";
1703 GetTvShowsByWhere("videodb://2/2/", filter, items);
1706 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
1709 filter.join = "LEFT JOIN actorlinkepisode ON actorlinkepisode.idEpisode=episodeview.idEpisode "
1710 "LEFT JOIN actors a ON a.idActor=actorlinkepisode.idActor "
1711 "LEFT JOIN directorlinkepisode ON directorlinkepisode.idEpisode=episodeview.idEpisode "
1712 "LEFT JOIN actors d ON d.idActor=directorlinkepisode.idDirector";
1713 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1714 filter.group = "episodeview.idEpisode";
1715 GetEpisodesByWhere("videodb://2/2/", filter, items);
1718 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1723 if (NULL == m_pDB.get()) return ;
1724 if (NULL == m_pDS.get()) return ;
1727 if (strArtist.IsEmpty()) // TODO: SMARTPLAYLISTS what is this here for???
1728 strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1730 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());
1731 m_pDS->query( strSQL.c_str() );
1733 while (!m_pDS->eof())
1735 CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1736 CFileItemPtr pItem(new CFileItem(tag));
1737 pItem->SetLabel(StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator));
1745 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1749 //********************************************************************************************************************************
1750 bool CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
1754 // TODO: Optimize this - no need for all the queries!
1756 idMovie = GetMovieId(strFilenameAndPath);
1757 if (idMovie < 0) return false;
1759 CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
1760 if (!m_pDS->query(sql.c_str()))
1762 details = GetDetailsForMovie(m_pDS, true);
1763 return !details.IsEmpty();
1767 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1772 //********************************************************************************************************************************
1773 bool CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
1778 idTvShow = GetTvShowId(strPath);
1779 if (idTvShow < 0) return false;
1781 CStdString sql = PrepareSQL("SELECT * FROM tvshowview WHERE idShow=%i", idTvShow);
1782 if (!m_pDS->query(sql.c_str()))
1784 details = GetDetailsForTvShow(m_pDS, true);
1785 return !details.IsEmpty();
1789 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1794 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
1798 // TODO: Optimize this - no need for all the queries!
1800 idEpisode = GetEpisodeId(strFilenameAndPath);
1801 if (idEpisode < 0) return false;
1803 CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
1804 if (!m_pDS->query(sql.c_str()))
1806 details = GetDetailsForEpisode(m_pDS, true);
1807 return !details.IsEmpty();
1811 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1816 bool CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
1820 // TODO: Optimize this - no need for all the queries!
1822 idMVideo = GetMusicVideoId(strFilenameAndPath);
1823 if (idMVideo < 0) return false;
1825 CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
1826 if (!m_pDS->query(sql.c_str()))
1828 details = GetDetailsForMusicVideo(m_pDS, true);
1829 return !details.IsEmpty();
1833 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1838 bool CVideoDatabase::GetSetInfo(int idSet, CVideoInfoTag& details)
1846 filter.where = PrepareSQL("sets.idSet=%d", idSet);
1847 CFileItemList items;
1848 if (!GetSetsByWhere("videodb://1/7/", filter, items) ||
1849 items.Size() != 1 ||
1850 !items[0]->HasVideoInfoTag())
1853 details = *(items[0]->GetVideoInfoTag());
1854 return !details.IsEmpty();
1858 CLog::Log(LOGERROR, "%s (%d) failed", __FUNCTION__, idSet);
1863 bool CVideoDatabase::GetFileInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idFile /* = -1 */)
1868 idFile = GetFileId(strFilenameAndPath);
1872 CStdString sql = PrepareSQL("SELECT * FROM files "
1873 "JOIN path ON path.idPath = files.idPath "
1874 "LEFT JOIN bookmark ON bookmark.idFile = files.idFile AND bookmark.type = %i "
1875 "WHERE files.idFile = %i", CBookmark::RESUME, idFile);
1876 if (!m_pDS->query(sql.c_str()))
1879 details.m_iFileId = m_pDS->fv("files.idFile").get_asInt();
1880 details.m_strPath = m_pDS->fv("path.strPath").get_asString();
1881 CStdString strFileName = m_pDS->fv("files.strFilename").get_asString();
1882 ConstructPath(details.m_strFileNameAndPath, details.m_strPath, strFileName);
1883 details.m_playCount = max(details.m_playCount, m_pDS->fv("files.playCount").get_asInt());
1884 if (!details.m_lastPlayed.IsValid())
1885 details.m_lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
1886 if (!details.m_dateAdded.IsValid())
1887 details.m_dateAdded.SetFromDBDateTime(m_pDS->fv("files.dateAdded").get_asString());
1888 if (!details.m_resumePoint.IsSet())
1890 details.m_resumePoint.timeInSeconds = m_pDS->fv("bookmark.timeInSeconds").get_asInt();
1891 details.m_resumePoint.totalTimeInSeconds = m_pDS->fv("bookmark.totalTimeInSeconds").get_asInt();
1892 details.m_resumePoint.type = CBookmark::RESUME;
1895 return !details.IsEmpty();
1899 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1904 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1906 // add all directors
1907 for (unsigned int i = 0; i < details.m_director.size(); i++)
1908 vecDirectors.push_back(AddActor(details.m_director[i],""));
1911 for (unsigned int i = 0; i < details.m_genre.size(); i++)
1912 vecGenres.push_back(AddGenre(details.m_genre[i]));
1914 for (unsigned int i = 0; i < details.m_studio.size(); i++)
1915 vecStudios.push_back(AddStudio(details.m_studio[i]));
1918 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1921 for (int i = min + 1; i < max; ++i)
1923 switch (offsets[i].type)
1925 case VIDEODB_TYPE_STRING:
1926 sql += PrepareSQL("c%02d='%s',", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str());
1928 case VIDEODB_TYPE_INT:
1929 sql += PrepareSQL("c%02d='%i',", i, *(int*)(((char*)&details)+offsets[i].offset));
1931 case VIDEODB_TYPE_COUNT:
1933 int value = *(int*)(((char*)&details)+offsets[i].offset);
1935 sql += PrepareSQL("c%02d=%i,", i, value);
1937 sql += PrepareSQL("c%02d=NULL,", i);
1940 case VIDEODB_TYPE_BOOL:
1941 sql += PrepareSQL("c%02d='%s',", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false");
1943 case VIDEODB_TYPE_FLOAT:
1944 sql += PrepareSQL("c%02d='%f',", i, *(float*)(((char*)&details)+offsets[i].offset));
1946 case VIDEODB_TYPE_STRINGARRAY:
1947 sql += PrepareSQL("c%02d='%s',", i, StringUtils::Join(*((std::vector<std::string>*)(((char*)&details)+offsets[i].offset)), g_advancedSettings.m_videoItemSeparator).c_str());
1949 case VIDEODB_TYPE_DATE:
1950 sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDate().c_str());
1952 case VIDEODB_TYPE_DATETIME:
1953 sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDateTime().c_str());
1961 //********************************************************************************************************************************
1962 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMovie /* = -1 */)
1969 idMovie = GetMovieId(strFilenameAndPath);
1972 DeleteMovie(strFilenameAndPath, true, idMovie); // true to keep the table entry
1975 // only add a new movie if we don't already have a valid idMovie
1976 // (DeleteMovie is called with bKeepId == true so the movie won't
1977 // be removed from the movie table)
1978 idMovie = AddMovie(strFilenameAndPath);
1981 CommitTransaction();
1986 vector<int> vecDirectors;
1987 vector<int> vecGenres;
1988 vector<int> vecStudios;
1989 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1991 for (unsigned int i = 0; i < vecGenres.size(); ++i)
1992 AddGenreToMovie(idMovie, vecGenres[i]);
1994 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
1995 AddDirectorToMovie(idMovie, vecDirectors[i]);
1997 for (unsigned int i = 0; i < vecStudios.size(); ++i)
1998 AddStudioToMovie(idMovie, vecStudios[i]);
2001 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2002 AddWriterToMovie(idMovie, AddActor(details.m_writingCredits[i],""));
2006 for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
2008 int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
2009 AddActorToMovie(idMovie, idActor, it->strRole, order++);
2014 if (!details.m_strSet.empty())
2016 idSet = AddSet(details.m_strSet);
2017 // add art if not available
2018 map<string, string> setArt;
2019 if (!GetArtForItem(idSet, "set", setArt))
2020 SetArtForItem(idSet, "set", artwork);
2024 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2026 int idTag = AddTag(details.m_tags[i]);
2027 AddTagToItem(idMovie, idTag, "movie");
2031 for (unsigned int i = 0; i < details.m_country.size(); i++)
2032 AddCountryToMovie(idMovie, AddCountry(details.m_country[i]));
2034 if (details.HasStreamDetails())
2035 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2037 SetArtForItem(idMovie, "movie", artwork);
2039 // update our movie table (we know it was added already above)
2040 // and insert the new row
2041 CStdString sql = "update movie set " + GetValueString(details, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
2043 sql += PrepareSQL(", idSet = %i", idSet);
2045 sql += ", idSet = NULL";
2046 sql += PrepareSQL(" where idMovie=%i", idMovie);
2047 m_pDS->exec(sql.c_str());
2048 CommitTransaction();
2054 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2059 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, map<string, string> > &seasonArt, int idTvShow /*= -1 */)
2063 if (!m_pDB.get() || !m_pDS.get())
2065 CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
2072 idTvShow = GetTvShowId(strPath);
2075 DeleteDetailsForTvShow(strPath, idTvShow);
2078 idTvShow = AddTvShow(strPath);
2081 CommitTransaction();
2086 vector<int> vecDirectors;
2087 vector<int> vecGenres;
2088 vector<int> vecStudios;
2089 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2093 for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
2095 int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
2096 AddActorToTvShow(idTvShow, idActor, it->strRole, order++);
2100 for (i = 0; i < vecGenres.size(); ++i)
2101 AddGenreToTvShow(idTvShow, vecGenres[i]);
2103 for (i = 0; i < vecDirectors.size(); ++i)
2104 AddDirectorToTvShow(idTvShow, vecDirectors[i]);
2106 for (i = 0; i < vecStudios.size(); ++i)
2107 AddStudioToTvShow(idTvShow, vecStudios[i]);
2110 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2112 int idTag = AddTag(details.m_tags[i]);
2113 AddTagToItem(idTvShow, idTag, "tvshow");
2116 // add "all seasons" - the rest are added in SetDetailsForEpisode
2117 AddSeason(idTvShow, -1);
2119 SetArtForItem(idTvShow, "tvshow", artwork);
2120 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
2122 int idSeason = AddSeason(idTvShow, i->first);
2124 SetArtForItem(idSeason, "season", i->second);
2127 // and insert the new row
2128 CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
2129 sql += PrepareSQL(" where idShow=%i", idTvShow);
2130 m_pDS->exec(sql.c_str());
2132 CommitTransaction();
2138 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2144 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idShow, int idEpisode)
2150 idEpisode = GetEpisodeId(strFilenameAndPath);
2153 DeleteEpisode(strFilenameAndPath, idEpisode, true); // true to keep the table entry
2156 // only add a new episode if we don't already have a valid idEpisode
2157 // (DeleteEpisode is called with bKeepId == true so the episode won't
2158 // be removed from the episode table)
2159 idEpisode = AddEpisode(idShow,strFilenameAndPath);
2162 CommitTransaction();
2167 vector<int> vecDirectors;
2168 vector<int> vecGenres;
2169 vector<int> vecStudios;
2170 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2174 for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
2176 int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
2177 AddActorToEpisode(idEpisode, idActor, it->strRole, order++);
2181 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2182 AddWriterToEpisode(idEpisode, AddActor(details.m_writingCredits[i],""));
2184 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2186 AddDirectorToEpisode(idEpisode, vecDirectors[i]);
2189 if (details.HasStreamDetails())
2191 if (details.m_iFileId != -1)
2192 SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2194 SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
2197 // ensure we have this season already added
2198 AddSeason(idShow, details.m_iSeason);
2200 SetArtForItem(idEpisode, "episode", artwork);
2202 // and insert the new row
2203 CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
2204 sql += PrepareSQL(" where idEpisode=%i", idEpisode);
2205 m_pDS->exec(sql.c_str());
2206 CommitTransaction();
2212 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2217 int CVideoDatabase::GetSeasonId(int showID, int season)
2219 CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
2220 CStdString id = GetSingleValue("seasons", "idSeason", sql);
2223 return strtol(id.c_str(), NULL, 10);
2226 int CVideoDatabase::AddSeason(int showID, int season)
2228 int seasonId = GetSeasonId(showID, season);
2231 if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
2232 seasonId = (int)m_pDS->lastinsertid();
2237 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
2244 idMVideo = GetMusicVideoId(strFilenameAndPath);
2247 DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
2250 // only add a new musicvideo if we don't already have a valid idMVideo
2251 // (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
2252 // be removed from the musicvideo table)
2253 idMVideo = AddMusicVideo(strFilenameAndPath);
2256 CommitTransaction();
2261 vector<int> vecDirectors;
2262 vector<int> vecGenres;
2263 vector<int> vecStudios;
2264 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2267 if (!details.m_artist.empty())
2269 for (unsigned int i = 0; i < details.m_artist.size(); i++)
2271 CStdString artist = details.m_artist[i];
2273 int idArtist = AddActor(artist,"");
2274 AddArtistToMusicVideo(idMVideo, idArtist);
2279 for (i = 0; i < vecGenres.size(); ++i)
2281 AddGenreToMusicVideo(idMVideo, vecGenres[i]);
2284 for (i = 0; i < vecDirectors.size(); ++i)
2286 AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
2289 for (i = 0; i < vecStudios.size(); ++i)
2291 AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2295 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2297 int idTag = AddTag(details.m_tags[i]);
2298 AddTagToItem(idMVideo, idTag, "musicvideo");
2301 if (details.HasStreamDetails())
2302 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2304 SetArtForItem(idMVideo, "musicvideo", artwork);
2306 // update our movie table (we know it was added already above)
2307 // and insert the new row
2308 CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
2309 sql += PrepareSQL(" where idMVideo=%i", idMVideo);
2310 m_pDS->exec(sql.c_str());
2311 CommitTransaction();
2317 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2322 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2324 // AddFile checks to make sure the file isn't already in the DB first
2325 int idFile = AddFile(strFileNameAndPath);
2328 SetStreamDetailsForFileId(details, idFile);
2331 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2339 m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2341 for (int i=1; i<=details.GetVideoStreamCount(); i++)
2343 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2344 "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration) "
2345 "VALUES (%i,%i,'%s',%f,%i,%i,%i)",
2346 idFile, (int)CStreamDetail::VIDEO,
2347 details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2348 details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i)));
2350 for (int i=1; i<=details.GetAudioStreamCount(); i++)
2352 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2353 "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2354 "VALUES (%i,%i,'%s',%i,'%s')",
2355 idFile, (int)CStreamDetail::AUDIO,
2356 details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2357 details.GetAudioLanguage(i).c_str()));
2359 for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2361 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2362 "(idFile, iStreamType, strSubtitleLanguage) "
2363 "VALUES (%i,%i,'%s')",
2364 idFile, (int)CStreamDetail::SUBTITLE,
2365 details.GetSubtitleLanguage(i).c_str()));
2368 // update the runtime information, if empty
2369 if (details.GetVideoDuration())
2371 vector< pair<string, int> > tables;
2372 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
2373 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
2374 tables.push_back(make_pair("musicvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
2375 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
2377 CStdString sql = PrepareSQL("update %s set c%02d=%d where idFile=%d and c%02d=''",
2378 i->first.c_str(), i->second, details.GetVideoDuration(), idFile, i->second);
2383 CommitTransaction();
2387 RollbackTransaction();
2388 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2392 //********************************************************************************************************************************
2393 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2397 if (NULL == m_pDB.get()) return ;
2398 if (NULL == m_pDS.get()) return ;
2400 if (idMovie < 0) return ;
2403 if (iType == VIDEODB_CONTENT_MOVIES)
2404 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 );
2405 if (iType == VIDEODB_CONTENT_EPISODES)
2406 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 );
2407 if (iType == VIDEODB_CONTENT_TVSHOWS)
2408 strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2409 if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2410 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 );
2412 m_pDS->query( strSQL.c_str() );
2415 if (iType != VIDEODB_CONTENT_TVSHOWS)
2417 CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2418 ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2421 filePath = m_pDS->fv("path.strPath").get_asString();
2427 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2431 //********************************************************************************************************************************
2432 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend, long partNumber)
2436 if (URIUtils::IsStack(strFilenameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(strFilenameAndPath),false).IsDVDImage())
2438 CStackDirectory dir;
2439 CFileItemList fileList;
2440 dir.GetDirectory(strFilenameAndPath, fileList);
2443 for (int i = fileList.Size() - 1; i >= 0; i--) // put the bookmarks of the highest part first in the list
2444 GetBookMarksForFile(fileList[i]->GetPath(), bookmarks, type, true, (i+1));
2448 int idFile = GetFileId(strFilenameAndPath);
2449 if (idFile < 0) return ;
2451 bookmarks.erase(bookmarks.begin(), bookmarks.end());
2452 if (NULL == m_pDB.get()) return ;
2453 if (NULL == m_pDS.get()) return ;
2455 CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2456 m_pDS->query( strSQL.c_str() );
2457 while (!m_pDS->eof())
2460 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2461 bookmark.partNumber = partNumber;
2462 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2463 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2464 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2465 bookmark.player = m_pDS->fv("player").get_asString();
2466 bookmark.type = type;
2467 if (type == CBookmark::EPISODE)
2469 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);
2470 m_pDS2->query(strSQL2.c_str());
2471 bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2472 bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2475 bookmarks.push_back(bookmark);
2478 //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2484 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2488 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2490 VECBOOKMARKS bookmarks;
2491 GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2492 if (bookmarks.size() > 0)
2494 bookmark = bookmarks[0];
2500 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2502 if (!m_pDB.get() || !m_pDS.get())
2505 int fileID = GetFileId(strFilenameAndPath);
2511 CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2512 m_pDS->exec(sql.c_str());
2516 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2520 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2524 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);
2525 m_pDS->query(strSQL.c_str());
2526 while (!m_pDS->eof())
2528 episodes.push_back(GetDetailsForEpisode(m_pDS));
2535 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2539 //********************************************************************************************************************************
2540 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2544 int idFile = AddFile(strFilenameAndPath);
2547 if (NULL == m_pDB.get()) return ;
2548 if (NULL == m_pDS.get()) return ;
2552 if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2554 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2556 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
2558 /* get a bookmark within the same time as previous */
2559 double mintime = bookmark.timeInSeconds - 0.5f;
2560 double maxtime = bookmark.timeInSeconds + 0.5f;
2561 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());
2564 if (type != CBookmark::EPISODE)
2567 m_pDS->query( strSQL.c_str() );
2568 if (m_pDS->num_rows() != 0)
2569 idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2572 // update or insert depending if it existed before
2573 if (idBookmark >= 0 )
2574 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);
2576 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);
2578 m_pDS->exec(strSQL.c_str());
2582 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2586 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2590 int idFile = GetFileId(strFilenameAndPath);
2591 if (idFile < 0) return ;
2592 if (NULL == m_pDB.get()) return ;
2593 if (NULL == m_pDS.get()) return ;
2595 /* a litle bit uggly, we clear first bookmark that is within one second of given */
2596 /* should be no problem since we never add bookmarks that are closer than that */
2597 double mintime = bookmark.timeInSeconds - 0.5f;
2598 double maxtime = bookmark.timeInSeconds + 0.5f;
2599 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);
2601 m_pDS->query( strSQL.c_str() );
2602 if (m_pDS->num_rows() != 0)
2604 int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2605 strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2606 m_pDS->exec(strSQL.c_str());
2607 if (type == CBookmark::EPISODE)
2609 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);
2610 m_pDS->exec(strSQL.c_str());
2618 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2622 //********************************************************************************************************************************
2623 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2627 int idFile = GetFileId(strFilenameAndPath);
2628 if (idFile < 0) return ;
2629 if (NULL == m_pDB.get()) return ;
2630 if (NULL == m_pDS.get()) return ;
2632 CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2633 m_pDS->exec(strSQL.c_str());
2634 if (type == CBookmark::EPISODE)
2636 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2637 m_pDS->exec(strSQL.c_str());
2642 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2647 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2651 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);
2652 m_pDS->query( strSQL.c_str() );
2655 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2656 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2657 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2658 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2659 bookmark.player = m_pDS->fv("player").get_asString();
2660 bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2671 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2677 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2681 int idFile = GetFileId(tag.m_strFileNameAndPath);
2682 // delete the current episode for the selected episode number
2683 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);
2684 m_pDS->exec(strSQL.c_str());
2686 AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2687 int idBookmark = (int)m_pDS->lastinsertid();
2688 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);
2689 m_pDS->exec(strSQL.c_str());
2693 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2697 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2701 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2702 m_pDS->exec(strSQL.c_str());
2703 strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2704 m_pDS->exec(strSQL.c_str());
2708 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2712 //********************************************************************************************************************************
2713 void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
2719 GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
2721 DeleteMovie(path, bKeepId, idMovie);
2724 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
2728 if (NULL == m_pDB.get()) return ;
2729 if (NULL == m_pDS.get()) return ;
2732 idMovie = GetMovieId(strFilenameAndPath);
2740 strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2741 m_pDS->exec(strSQL.c_str());
2743 strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2744 m_pDS->exec(strSQL.c_str());
2746 strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2747 m_pDS->exec(strSQL.c_str());
2749 strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2750 m_pDS->exec(strSQL.c_str());
2752 strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2753 m_pDS->exec(strSQL.c_str());
2755 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2757 // keep the movie table entry, linking to tv shows, and bookmarks
2758 // so we can update the data in place
2759 // the ancilliary tables are still purged
2762 ClearBookMarksOfFile(strFilenameAndPath);
2764 strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2765 m_pDS->exec(strSQL.c_str());
2767 strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2768 m_pDS->exec(strSQL.c_str());
2771 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2773 AnnounceRemove("movie", idMovie);
2775 CStdString strPath, strFileName;
2776 SplitPath(strFilenameAndPath,strPath,strFileName);
2777 InvalidatePathHash(strPath);
2778 CommitTransaction();
2783 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2787 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2793 GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2795 DeleteTvShow(path, bKeepId, idTvShow);
2798 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2802 if (NULL == m_pDB.get()) return ;
2803 if (NULL == m_pDS.get()) return ;
2806 idTvShow = GetTvShowId(strPath);
2813 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);
2814 m_pDS2->query(strSQL.c_str());
2815 while (!m_pDS2->eof())
2817 CStdString strFilenameAndPath;
2818 CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2819 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2820 ConstructPath(strFilenameAndPath, strPath, strFileName);
2821 DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2825 DeleteDetailsForTvShow(strPath, idTvShow);
2827 strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2828 m_pDS->exec(strSQL.c_str());
2830 // keep tvshow table and movielink table so we can update data in place
2833 strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2834 m_pDS->exec(strSQL.c_str());
2836 strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2837 m_pDS->exec(strSQL.c_str());
2839 strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2840 m_pDS->exec(strSQL.c_str());
2843 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2845 AnnounceRemove("tvshow", idTvShow);
2847 InvalidatePathHash(strPath);
2849 CommitTransaction();
2854 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2858 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2864 GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2866 DeleteEpisode(path, idEpisode, bKeepId);
2869 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
2873 if (NULL == m_pDB.get()) return ;
2874 if (NULL == m_pDS.get()) return ;
2877 idEpisode = GetEpisodeId(strFilenameAndPath);
2884 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2886 AnnounceRemove("episode", idEpisode);
2889 strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
2890 m_pDS->exec(strSQL.c_str());
2892 strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
2893 m_pDS->exec(strSQL.c_str());
2895 strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
2896 m_pDS->exec(strSQL.c_str());
2898 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2900 // keep episode table entry and bookmarks so we can update the data in place
2901 // the ancilliary tables are still purged
2904 ClearBookMarksOfFile(strFilenameAndPath);
2906 strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
2907 m_pDS->exec(strSQL.c_str());
2913 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2917 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
2919 if (idMusicVideo < 0)
2923 GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
2925 DeleteMusicVideo(path, bKeepId, idMusicVideo);
2928 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
2932 if (NULL == m_pDB.get()) return ;
2933 if (NULL == m_pDS.get()) return ;
2936 idMVideo = GetMusicVideoId(strFilenameAndPath);
2944 strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
2945 m_pDS->exec(strSQL.c_str());
2947 strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
2948 m_pDS->exec(strSQL.c_str());
2950 strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
2951 m_pDS->exec(strSQL.c_str());
2953 strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
2954 m_pDS->exec(strSQL.c_str());
2956 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2958 // keep the music video table entry and bookmarks so we can update data in place
2959 // the ancilliary tables are still purged
2962 ClearBookMarksOfFile(strFilenameAndPath);
2964 strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
2965 m_pDS->exec(strSQL.c_str());
2968 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2970 AnnounceRemove("musicvideo", idMVideo);
2972 CStdString strPath, strFileName;
2973 SplitPath(strFilenameAndPath,strPath,strFileName);
2974 InvalidatePathHash(strPath);
2975 CommitTransaction();
2980 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2984 void CVideoDatabase::DeleteStreamDetails(int idFile)
2986 m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
2989 void CVideoDatabase::DeleteSet(int idSet)
2993 if (NULL == m_pDB.get()) return ;
2994 if (NULL == m_pDS.get()) return ;
2997 strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
2998 m_pDS->exec(strSQL.c_str());
2999 strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3000 m_pDS->exec(strSQL.c_str());
3004 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3008 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3012 if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3016 if (mediaType == VIDEODB_CONTENT_MOVIES)
3018 else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3020 else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3021 type = "musicvideo";
3026 strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3027 m_pDS->exec(strSQL.c_str());
3031 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3035 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3037 GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3040 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3042 for (int i = min + 1; i < max; i++)
3044 switch (offsets[i].type)
3046 case VIDEODB_TYPE_STRING:
3047 *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3049 case VIDEODB_TYPE_INT:
3050 case VIDEODB_TYPE_COUNT:
3051 *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3053 case VIDEODB_TYPE_BOOL:
3054 *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3056 case VIDEODB_TYPE_FLOAT:
3057 *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3059 case VIDEODB_TYPE_STRINGARRAY:
3060 *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3062 case VIDEODB_TYPE_DATE:
3063 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3065 case VIDEODB_TYPE_DATETIME:
3066 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3072 DWORD movieTime = 0;
3075 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3077 CVideoInfoTag details;
3082 case VIDEODB_CONTENT_MOVIES:
3083 GetMovieInfo("", details, id);
3085 case VIDEODB_CONTENT_TVSHOWS:
3086 GetTvShowInfo("", details, id);
3088 case VIDEODB_CONTENT_EPISODES:
3089 GetEpisodeInfo("", details, id);
3091 case VIDEODB_CONTENT_MUSICVIDEOS:
3092 GetMusicVideoInfo("", details, id);
3101 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3103 if (tag.m_iFileId < 0)
3106 bool retVal = false;
3108 CStreamDetails& details = tag.m_streamDetails;
3111 auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3114 CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3119 CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3122 case CStreamDetail::VIDEO:
3124 CStreamDetailVideo *p = new CStreamDetailVideo();
3125 p->m_strCodec = pDS->fv(2).get_asString();
3126 p->m_fAspect = pDS->fv(3).get_asFloat();
3127 p->m_iWidth = pDS->fv(4).get_asInt();
3128 p->m_iHeight = pDS->fv(5).get_asInt();
3129 p->m_iDuration = pDS->fv(10).get_asInt();
3130 details.AddStream(p);
3134 case CStreamDetail::AUDIO:
3136 CStreamDetailAudio *p = new CStreamDetailAudio();
3137 p->m_strCodec = pDS->fv(6).get_asString();
3138 if (pDS->fv(7).get_isNull())
3139 p->m_iChannels = -1;
3141 p->m_iChannels = pDS->fv(7).get_asInt();
3142 p->m_strLanguage = pDS->fv(8).get_asString();
3143 details.AddStream(p);
3147 case CStreamDetail::SUBTITLE:
3149 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3150 p->m_strLanguage = pDS->fv(9).get_asString();
3151 details.AddStream(p);
3164 CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3166 details.DetermineBestStreams();
3168 if (details.GetVideoDuration() > 0)
3169 tag.m_duration = details.GetVideoDuration();
3174 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3176 if (tag.m_iFileId < 0)
3183 if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3185 CStackDirectory dir;
3186 CFileItemList fileList;
3187 dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3188 tag.m_resumePoint.Reset();
3189 for (int i = fileList.Size() - 1; i >= 0; i--)
3192 if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3194 tag.m_resumePoint = bookmark;
3195 tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3203 CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3204 m_pDS2->query( strSQL.c_str() );
3207 tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3208 tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3209 tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3210 tag.m_resumePoint.type = CBookmark::RESUME;
3218 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3224 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3226 return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3229 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3231 CVideoInfoTag details;
3236 DWORD time = XbmcThreads::SystemClockMillis();
3237 int idMovie = record->at(0).get_asInt();
3239 GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3241 details.m_iDbId = idMovie;
3242 details.m_type = "movie";
3244 details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3245 details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3246 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3247 details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3248 CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3249 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3250 details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3251 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3252 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3253 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3254 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3255 details.m_resumePoint.type = CBookmark::RESUME;
3257 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3261 GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3263 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3264 details.m_strPictureURL.Parse();
3267 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);
3268 m_pDS2->query(strSQL.c_str());
3269 while (!m_pDS2->eof())
3271 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3275 // create tvshowlink string
3277 GetLinksToTvShow(idMovie,links);
3278 for (unsigned int i=0;i<links.size();++i)
3280 CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3281 VIDEODB_ID_TV_TITLE,links[i]);
3282 m_pDS2->query(strSQL.c_str());
3284 details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3288 // get streamdetails
3289 GetStreamDetails(details);
3294 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3296 return GetDetailsForTvShow(pDS->get_sql_record(), getDetails);
3299 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3301 CVideoInfoTag details;
3306 DWORD time = XbmcThreads::SystemClockMillis();
3307 int idTvShow = record->at(0).get_asInt();
3309 GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3310 details.m_iDbId = idTvShow;
3311 details.m_type = "tvshow";
3312 details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3313 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3314 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3315 details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3316 details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3317 details.m_strShowPath = details.m_strPath;
3318 details.m_strShowTitle = details.m_strTitle;
3320 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3324 GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3327 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);
3328 m_pDS2->query(strSQL.c_str());
3329 while (!m_pDS2->eof())
3331 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3335 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3336 details.m_strPictureURL.Parse();
3341 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3343 return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3346 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3348 CVideoInfoTag details;
3353 DWORD time = XbmcThreads::SystemClockMillis();
3354 int idEpisode = record->at(0).get_asInt();
3356 GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3357 details.m_iDbId = idEpisode;
3358 details.m_type = "episode";
3359 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3360 details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3361 CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3362 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3363 details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3364 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3365 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3366 details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3367 details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3368 details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3369 details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3370 details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3371 details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3372 details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3374 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3375 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3376 details.m_resumePoint.type = CBookmark::RESUME;
3378 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3382 GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3383 GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3385 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3386 details.m_strPictureURL.Parse();
3387 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);
3388 m_pDS2->query(strSQL.c_str());
3390 details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3393 // get streamdetails
3394 GetStreamDetails(details);
3399 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3401 return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3404 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3406 CVideoInfoTag details;
3408 unsigned int time = XbmcThreads::SystemClockMillis();
3409 int idMVideo = record->at(0).get_asInt();
3411 GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3412 details.m_iDbId = idMVideo;
3413 details.m_type = "musicvideo";
3415 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3416 details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3417 CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3418 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3419 details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3420 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3421 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3422 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3423 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3424 details.m_resumePoint.type = CBookmark::RESUME;
3426 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3431 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);
3432 m_pDS2->query(strSQL.c_str());
3433 while (!m_pDS2->eof())
3435 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3440 details.m_strPictureURL.Parse();
3442 // get streamdetails
3443 GetStreamDetails(details);
3448 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3452 if (!m_pDB.get()) return;
3453 if (!m_pDS2.get()) return;
3455 CStdString sql = PrepareSQL("SELECT actors.strActor,"
3456 " actorlink%s.strRole,"
3461 " actorlink%s.idActor=actors.idActor"
3463 " art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3464 "WHERE actorlink%s.%s=%i "
3465 "ORDER BY actorlink%s.iOrder",table.c_str(), table.c_str(), table.c_str(), table.c_str(), table_id.c_str(), type_id, table.c_str());
3466 m_pDS2->query(sql.c_str());
3467 while (!m_pDS2->eof())
3470 info.strName = m_pDS2->fv(0).get_asString();
3472 for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3474 if (i->strName == info.strName)
3482 info.strRole = m_pDS2->fv(1).get_asString();
3483 info.thumbUrl.ParseString(m_pDS2->fv(2).get_asString());
3484 info.thumb = m_pDS2->fv(3).get_asString();
3485 cast.push_back(info);
3493 CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3497 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3498 /// \retval Returns true if the settings exist, false otherwise.
3499 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3503 // obtain the FileID (if it exists)
3504 #ifdef NEW_VIDEODB_METHODS
3505 if (NULL == m_pDB.get()) return false;
3506 if (NULL == m_pDS.get()) return false;
3507 CStdString strPath, strFileName;
3508 URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3509 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());
3511 int idFile = GetFileId(strFilenameAndPath);
3512 if (idFile < 0) return false;
3513 if (NULL == m_pDB.get()) return false;
3514 if (NULL == m_pDS.get()) return false;
3515 // ok, now obtain the settings for this file
3516 CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3518 m_pDS->query( strSQL.c_str() );
3519 if (m_pDS->num_rows() > 0)
3520 { // get the video settings info
3521 settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3522 settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3523 settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3524 settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3525 settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3526 settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3527 settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3528 settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3529 settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3530 settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3531 settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3532 settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3533 settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3534 settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3535 settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3536 settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3537 settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3538 settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3539 settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3540 settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3541 settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3542 settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3543 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3544 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3545 settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3546 settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3547 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3548 settings.m_SubtitleCached = false;
3556 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3561 /// \brief Sets the settings for a particular video file
3562 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3566 if (NULL == m_pDB.get()) return ;
3567 if (NULL == m_pDS.get()) return ;
3568 int idFile = AddFile(strFilenameAndPath);
3572 strSQL.Format("select * from settings where idFile=%i", idFile);
3573 m_pDS->query( strSQL.c_str() );
3574 if (m_pDS->num_rows() > 0)
3578 strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3579 "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3580 "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3581 "DeinterlaceMode=%i,",
3582 setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3583 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3584 setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3585 setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3586 setting.m_DeinterlaceMode);
3588 strSQL2=PrepareSQL("ResumeTime=%i,Crop=%i,CropLeft=%i,CropRight=%i,CropTop=%i,CropBottom=%i where idFile=%i\n", setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom, idFile);
3590 m_pDS->exec(strSQL.c_str());
3596 strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3597 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3598 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3599 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3600 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode) "
3602 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)",
3603 idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3604 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3605 setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3606 setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3607 setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3608 setting.m_DeinterlaceMode);
3609 m_pDS->exec(strSQL.c_str());
3614 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3618 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3620 for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3621 SetArtForItem(mediaId, mediaType, i->first, i->second);
3624 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3628 if (NULL == m_pDB.get()) return;
3629 if (NULL == m_pDS.get()) return;
3631 // don't set <foo>.<bar> art types - these are derivative types from parent items
3632 if (artType.find('.') != string::npos)
3635 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());
3636 m_pDS->query(sql.c_str());
3639 int artId = m_pDS->fv(0).get_asInt();
3641 sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3642 m_pDS->exec(sql.c_str());
3647 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());
3648 m_pDS->exec(sql.c_str());
3653 CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3657 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3661 if (NULL == m_pDB.get()) return false;
3662 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3664 CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3665 m_pDS2->query(sql.c_str());
3666 while (!m_pDS2->eof())
3668 art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3672 return !art.empty();
3676 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3681 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3683 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());
3684 return GetSingleValue(query, m_pDS2);
3687 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3691 if (NULL == m_pDB.get()) return false;
3692 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3694 // get all seasons for this show
3695 CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3696 m_pDS2->query(sql.c_str());
3698 vector< pair<int, int> > seasons;
3699 while (!m_pDS2->eof())
3701 seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3706 for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3708 map<string, string> art;
3709 GetArtForItem(i->first, "season", art);
3710 seasonArt.insert(make_pair(i->second,art));
3716 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3721 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3722 /// \retval Returns true if the stack times exist, false otherwise.
3723 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> ×)
3727 // obtain the FileID (if it exists)
3728 int idFile = GetFileId(filePath);
3729 if (idFile < 0) return false;
3730 if (NULL == m_pDB.get()) return false;
3731 if (NULL == m_pDS.get()) return false;
3732 // ok, now obtain the settings for this file
3733 CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3734 m_pDS->query( strSQL.c_str() );
3735 if (m_pDS->num_rows() > 0)
3736 { // get the video settings info
3737 CStdStringArray timeString;
3739 StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3741 for (unsigned int i = 0; i < timeString.size(); i++)
3743 times.push_back(atoi(timeString[i].c_str()));
3744 timeTotal += atoi(timeString[i].c_str());
3747 return (timeTotal > 0);
3753 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3758 /// \brief Sets the stack times for a particular video file
3759 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> ×)
3763 if (NULL == m_pDB.get()) return ;
3764 if (NULL == m_pDS.get()) return ;
3765 int idFile = AddFile(filePath);
3769 // delete any existing items
3770 m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3773 CStdString timeString;
3774 timeString.Format("%i", times[0]);
3775 for (unsigned int i = 1; i < times.size(); i++)
3778 time.Format(",%i", times[i]);
3781 m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3785 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3789 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
3791 if(URIUtils::IsMultiPath(strPath))
3793 vector<CStdString> paths;
3794 CMultiPathDirectory::GetPaths(strPath, paths);
3796 for(unsigned i=0;i<paths.size();i++)
3797 RemoveContentForPath(paths[i], progress);
3802 if (NULL == m_pDB.get()) return ;
3803 if (NULL == m_pDS.get()) return ;
3807 progress->SetHeading(700);
3808 progress->SetLine(0, "");
3809 progress->SetLine(1, 313);
3810 progress->SetLine(2, 330);
3811 progress->SetPercentage(0);
3812 progress->StartModal();
3813 progress->ShowProgressBar(true);
3815 vector< pair<int,string> > paths;
3816 GetSubPaths(strPath, paths);
3818 for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
3820 bool bMvidsChecked=false;
3823 progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
3824 progress->Progress();
3827 if (HasTvShowInfo(i->second))
3828 DeleteTvShow(i->second);
3831 CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
3832 m_pDS2->query(strSQL.c_str());
3835 strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
3836 m_pDS2->query(strSQL.c_str());
3837 bMvidsChecked = true;
3839 while (!m_pDS2->eof())
3841 CStdString strMoviePath;
3842 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
3843 ConstructPath(strMoviePath, i->second, strFileName);
3844 if (HasMovieInfo(strMoviePath))
3845 DeleteMovie(strMoviePath);
3846 if (HasMusicVideoInfo(strMoviePath))
3847 DeleteMusicVideo(strMoviePath);
3849 if (m_pDS2->eof() && !bMvidsChecked)
3851 strSQL =PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
3852 m_pDS2->query(strSQL.c_str());
3853 bMvidsChecked = true;
3857 m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
3863 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
3869 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
3871 // if we have a multipath, set scraper for all contained paths too
3872 if(URIUtils::IsMultiPath(filePath))
3874 vector<CStdString> paths;
3875 CMultiPathDirectory::GetPaths(filePath, paths);
3877 for(unsigned i=0;i<paths.size();i++)
3878 SetScraperForPath(paths[i],scraper,settings);
3883 if (NULL == m_pDB.get()) return ;
3884 if (NULL == m_pDS.get()) return ;
3886 int idPath = AddPath(filePath);
3892 if (settings.exclude)
3893 { //NB See note in ::GetScraperForPath about strContent=='none'
3894 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
3897 { // catch clearing content, but not excluding
3898 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
3902 CStdString content = TranslateContent(scraper->Content());
3903 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);
3905 m_pDS->exec(strSQL.c_str());
3909 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3913 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
3917 if (NULL == m_pDB.get()) return false;
3918 if (NULL == m_pDS.get()) return false;
3920 CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
3921 if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
3923 bool found = m_pDS->fv(0).get_asInt() > 0;
3929 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
3937 CArtItem() { art_id = 0; media_id = 0; };
3945 bool CVideoDatabase::UpdateOldVersion(int iVersion)
3949 m_pDS->exec("ALTER TABLE settings ADD VerticalShift float");
3953 // only if MySQL is used and default character set is not utf8
3954 // string data needs to be converted to proper utf8
3955 CStdString charset = m_pDS->getDatabase()->getDefaultCharset();
3956 if (!m_sqlite && !charset.empty() && charset != "utf8")
3958 map<CStdString, CStdStringArray> tables;
3959 map<CStdString, CStdStringArray>::iterator itt;
3960 CStdStringArray::iterator itc;
3962 // columns that need to be converted
3964 CStdStringArray c_columns;
3965 for (int i = 0; i < 22; i++)
3968 c.Format("c%02d", i);
3969 c_columns.push_back(c);
3972 tables.insert(pair<CStdString, CStdStringArray> ("episode", c_columns));
3973 tables.insert(pair<CStdString, CStdStringArray> ("movie", c_columns));
3974 tables.insert(pair<CStdString, CStdStringArray> ("musicvideo", c_columns));
3975 tables.insert(pair<CStdString, CStdStringArray> ("tvshow", c_columns));
3979 c1.push_back("strRole");
3980 tables.insert(pair<CStdString, CStdStringArray> ("actorlinkepisode", c1));
3981 tables.insert(pair<CStdString, CStdStringArray> ("actorlinkmovie", c1));
3982 tables.insert(pair<CStdString, CStdStringArray> ("actorlinktvshow", c1));
3986 c2.push_back("strActor");
3987 tables.insert(pair<CStdString, CStdStringArray> ("actors", c2));
3990 c3.push_back("strCountry");
3991 tables.insert(pair<CStdString, CStdStringArray> ("country", c3));
3994 c4.push_back("strFilename");
3995 tables.insert(pair<CStdString, CStdStringArray> ("files", c4));
3998 c5.push_back("strGenre");
3999 tables.insert(pair<CStdString, CStdStringArray> ("genre", c5));
4002 c6.push_back("strSet");
4003 tables.insert(pair<CStdString, CStdStringArray> ("sets", c6));
4006 c7.push_back("strStudio");
4007 tables.insert(pair<CStdString, CStdStringArray> ("studio", c7));
4010 c8.push_back("strPath");
4011 tables.insert(pair<CStdString, CStdStringArray> ("path", c8));
4013 for (itt = tables.begin(); itt != tables.end(); ++itt)
4016 q = PrepareSQL("UPDATE `%s` SET", itt->first.c_str());
4017 for (itc = itt->second.begin(); itc != itt->second.end(); ++itc)
4019 q += PrepareSQL(" `%s` = CONVERT(CAST(CONVERT(`%s` USING %s) AS BINARY) USING utf8)",
4020 itc->c_str(), itc->c_str(), charset.c_str());
4021 if (*itc != itt->second.back())
4032 m_pDS->exec("ALTER TABLE movie ADD c22 text");
4033 m_pDS->exec("ALTER TABLE episode ADD c22 text");
4034 m_pDS->exec("ALTER TABLE musicvideo ADD c22 text");
4035 m_pDS->exec("ALTER TABLE tvshow ADD c22 text");
4036 // Now update our tables
4037 UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH);
4038 UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH);
4039 UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH);
4040 UpdateBasePath("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, true);
4043 { // add indices for dir entry lookups
4044 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c22(255) )");
4045 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c13(255) )");
4046 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c18(255) )");
4047 m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c16(255) )");
4051 m_pDS->exec("ALTER TABLE settings ADD ScalingMethod integer");
4052 m_pDS->exec(PrepareSQL("UPDATE settings set ScalingMethod=%i", g_settings.m_defaultVideoSettings.m_ScalingMethod));
4056 // Add iOrder fields to actorlink* tables to be able to list
4057 // actors by importance
4058 m_pDS->exec("ALTER TABLE actorlinkmovie ADD iOrder integer");
4059 m_pDS->exec("ALTER TABLE actorlinktvshow ADD iOrder integer");
4060 m_pDS->exec("ALTER TABLE actorlinkepisode ADD iOrder integer");
4063 { // Add basepath link to path table for faster content retrieval, and indicies
4064 m_pDS->exec("ALTER TABLE movie ADD c23 text");
4065 m_pDS->exec("ALTER TABLE episode ADD c23 text");
4066 m_pDS->exec("ALTER TABLE musicvideo ADD c23 text");
4067 m_pDS->exec("ALTER TABLE tvshow ADD c23 text");
4068 m_pDS->dropIndex("movie", "ixMovieBasePath");
4069 m_pDS->dropIndex("musicvideo", "ixMusicVideoBasePath");
4070 m_pDS->dropIndex("episode", "ixEpisodeBasePath");
4071 m_pDS->dropIndex("tvshow", "ixTVShowBasePath");
4072 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
4073 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
4074 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
4075 m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c17(12) )");
4076 // now update the base path links
4077 UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4078 UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4079 UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4080 UpdateBasePathID("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, VIDEODB_ID_TV_PARENTPATHID);
4083 { // Change INDEX for bookmark table
4084 m_pDS->dropIndex("bookmark", "ix_bookmark");
4085 m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
4089 m_pDS->exec("ALTER TABLE settings ADD DeinterlaceMode integer");
4090 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 2 WHERE Deinterlace NOT IN (0,1)"); // anything other than none: method auto => mode force
4091 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 1 WHERE Deinterlace = 1"); // method auto => mode auto
4092 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 0, Deinterlace = 1 WHERE Deinterlace = 0"); // method none => mode off, method auto
4096 { // base paths for video_ts and bdmv files was wrong (and inconsistent depending on where and when they were scanned)
4097 CStdString where = PrepareSQL(" WHERE files.strFileName LIKE 'VIDEO_TS.IFO' or files.strFileName LIKE 'index.BDMV'");
4098 UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH, false, where);
4099 UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, false, where);
4100 UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, false, where);
4101 UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4102 UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4103 UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4107 m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4108 m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4111 { // add seasons table
4112 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4113 m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
4114 // insert all seasons for each show
4115 m_pDS->query("SELECT idShow FROM tvshow");
4116 while (!m_pDS->eof())
4118 CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4123 " WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4124 m_pDS2->exec(sql.c_str());
4125 // and the "all seasons node"
4126 sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4127 m_pDS2->exec(sql.c_str());
4133 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4134 m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
4135 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; END");
4136 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; END");
4137 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; END");
4138 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; END");
4139 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; END");
4140 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; END");
4141 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); END");
4143 g_settings.m_videoNeedsUpdate = 63;
4147 { // add idShow to episode table
4148 m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4149 m_pDS->query("SELECT idEpisode FROM episode");
4150 while (!m_pDS->eof())
4152 int idEpisode = m_pDS->fv(0).get_asInt();
4153 CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4154 m_pDS2->exec(update.c_str());
4157 m_pDS->exec("DROP TABLE tvshowlinkepisode");
4158 m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
4159 m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
4163 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4164 m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
4166 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4167 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
4168 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
4169 m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
4172 { // add idSet to movie table
4173 m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4174 m_pDS->query("SELECT idMovie FROM movie");
4175 while (!m_pDS->eof())
4177 int idMovie = m_pDS->fv(0).get_asInt();
4178 CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4179 m_pDS2->exec(sql.c_str());
4182 m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4185 { // update old art URLs
4186 m_pDS->query("select art_id,url from art where url like 'image://%%'");
4187 vector< pair<int, string> > art;
4188 while (!m_pDS->eof())
4190 art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4194 for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4195 m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4198 { // update URL encoded paths
4199 m_pDS->query("select idFile, strFilename from files");
4200 vector< pair<int, string> > files;
4201 while (!m_pDS->eof())
4203 files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4208 for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4210 std::string filename = i->second;
4211 bool update = URIUtils::UpdateUrlEncoding(filename) &&
4212 (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4216 m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4220 { // Update thumb to poster or banner as applicable
4221 CTextureDatabase db;
4224 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')");
4225 vector<CArtItem> art;
4226 while (!m_pDS->eof())
4228 CTextureDetails details;
4229 if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4232 item.art_id = m_pDS->fv(0).get_asInt();
4233 item.art_url = m_pDS->fv(1).get_asString();
4234 item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4235 item.media_id = m_pDS->fv(2).get_asInt();
4236 item.media_type = m_pDS->fv(3).get_asString();
4237 if (item.art_type != "thumb")
4238 art.push_back(item);
4243 for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4245 if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4246 m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4248 m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4254 m_pDS->exec("DROP TRIGGER IF EXISTS delete_movie");
4255 m_pDS->exec("DROP TRIGGER IF EXISTS delete_tvshow");
4256 m_pDS->exec("DROP TRIGGER IF EXISTS delete_musicvideo");
4257 m_pDS->exec("DROP TRIGGER IF EXISTS delete_episode");
4258 m_pDS->exec("DROP TRIGGER IF EXISTS delete_season");
4259 m_pDS->exec("DROP TRIGGER IF EXISTS delete_set");
4260 m_pDS->exec("DROP TRIGGER IF EXISTS delete_person");
4262 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
4263 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
4264 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
4266 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
4267 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
4268 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
4270 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
4271 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
4272 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
4274 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
4275 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
4277 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
4278 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
4280 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
4281 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
4283 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
4284 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
4286 m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
4287 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
4291 { // update the runtime columns
4292 vector< pair<string, int> > tables;
4293 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4294 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4295 tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4296 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4298 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);
4299 m_pDS->query(sql.c_str());
4300 vector< pair<int, int> > videos;
4301 while (!m_pDS->eof())
4303 int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4305 videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4309 for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4310 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));
4314 { // make indices on path, file non-unique (mysql has a prefix index, and prefixes aren't necessarily unique)
4315 m_pDS->dropIndex("path", "ix_path");
4316 m_pDS->dropIndex("files", "ix_files");
4317 m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
4318 m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
4320 // always recreate the view after any table change
4325 int CVideoDatabase::GetMinVersion() const
4330 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4332 SScanSettings settings;
4333 bool foundDirectly = false;
4334 ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4335 if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4336 return false; // episodes
4337 return settings.parent_name_root; // shows, movies, musicvids
4340 void CVideoDatabase::UpdateBasePath(const char *table, const char *id, int column, bool shows, const CStdString &where)
4344 query = PrepareSQL("SELECT idShow,path.strPath from tvshowlinkpath join path on tvshowlinkpath.idPath=path.idPath");
4346 query = PrepareSQL("SELECT %s.%s,path.strPath,files.strFileName from %s join files on %s.idFile=files.idFile join path on files.idPath=path.idPath", table, id, table, table);
4349 map<CStdString, bool> paths;
4350 m_pDS2->query(query.c_str());
4351 while (!m_pDS2->eof())
4353 CStdString path(m_pDS2->fv(1).get_asString());
4354 map<CStdString, bool>::iterator i = paths.find(path);
4355 if (i == paths.end())
4357 paths.insert(make_pair(path, LookupByFolders(path, shows)));
4358 i = paths.find(path);
4360 CStdString filename;
4362 ConstructPath(filename, path, m_pDS2->fv(2).get_asString());
4365 CFileItem item(filename, shows);
4366 path = item.GetBaseMoviePath(i->second);
4367 CStdString sql = PrepareSQL("UPDATE %s set c%02d='%s' where %s.%s=%i", table, column, path.c_str(), table, id, m_pDS2->fv(0).get_asInt());
4368 m_pDS->exec(sql.c_str());
4374 void CVideoDatabase::UpdateBasePathID(const char *table, const char *id, int column, int idColumn)
4376 CStdString query = PrepareSQL("SELECT %s,c%02d from %s", id, column, table);
4377 m_pDS2->query(query.c_str());
4378 while (!m_pDS2->eof())
4380 int rowID = m_pDS2->fv(0).get_asInt();
4381 CStdString path(m_pDS2->fv(1).get_asString());
4382 // find the parent path of this item
4383 int pathID = AddPath(URIUtils::GetParentPath(path));
4386 CStdString sql = PrepareSQL("UPDATE %s SET c%02d=%d WHERE %s=%d", table, idColumn, pathID, id, rowID);
4387 m_pDS->exec(sql.c_str());
4394 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4396 if(URIUtils::IsMultiPath(strPath))
4398 vector<CStdString> paths;
4399 CMultiPathDirectory::GetPaths(strPath, paths);
4402 for(unsigned i=0;i<paths.size();i++)
4403 ret |= GetPlayCounts(paths[i], items);
4408 if (URIUtils::IsPlugin(strPath))
4411 pathID = GetPathId(url.GetWithoutFilename());
4414 pathID = GetPathId(strPath);
4416 return false; // path (and thus files) aren't in the database
4421 if (NULL == m_pDB.get()) return false;
4422 if (NULL == m_pDS.get()) return false;
4424 // TODO: also test a single query for the above and below
4425 CStdString sql = PrepareSQL(
4427 " files.strFilename, files.playCount,"
4428 " bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4430 " LEFT JOIN bookmark ON"
4431 " files.idFile = bookmark.idFile AND bookmark.type = %i"
4432 " WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4434 if (RunQuery(sql) <= 0)
4437 items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4438 while (!m_pDS->eof())
4441 ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4442 CFileItemPtr item = items.Get(path);
4445 item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4446 if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4448 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4449 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4450 item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4459 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4464 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4466 int id = GetFileId(item);
4468 return 0; // not in db, so not watched
4473 if (NULL == m_pDB.get()) return -1;
4474 if (NULL == m_pDS.get()) return -1;
4476 CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4478 if (m_pDS->query(strSQL.c_str()))
4480 // there should only ever be one row returned
4481 if (m_pDS->num_rows() == 1)
4482 count = m_pDS->fv(0).get_asInt();
4489 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4494 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4496 if (NULL == m_pDB.get()) return;
4497 if (NULL == m_pDS.get()) return;
4498 if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4501 if (type == VIDEODB_CONTENT_TVSHOWS)
4502 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);
4503 else if (type == VIDEODB_CONTENT_MOVIES)
4504 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);
4508 m_pDS->exec(exec.c_str());
4510 if (type == VIDEODB_CONTENT_TVSHOWS)
4511 AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4512 else if (type == VIDEODB_CONTENT_MOVIES)
4513 AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4517 CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4521 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4524 if (item.HasProperty("original_listitem_url") &&
4525 URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4527 CFileItem item2(item);
4528 item2.SetPath(item.GetProperty("original_listitem_url").asString());
4529 id = AddFile(item2);
4536 // and mark as watched
4539 if (NULL == m_pDB.get()) return ;
4540 if (NULL == m_pDS.get()) return ;
4545 if (!date.IsValid())
4546 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4548 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4552 if (!date.IsValid())
4553 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4555 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4558 m_pDS->exec(strSQL.c_str());
4560 // We only need to announce changes to video items in the library
4561 if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4563 // Only provide the "playcount" value if it has actually changed
4564 if (item.GetVideoInfoTag()->m_playCount != count)
4567 data["playcount"] = count;
4568 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4571 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4576 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4580 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4582 SetPlayCount(item, GetPlayCount(item) + 1);
4585 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4587 SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4590 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4594 if (NULL == m_pDB.get()) return ;
4595 if (NULL == m_pDS.get()) return ;
4598 if (iType == VIDEODB_CONTENT_MOVIES)
4600 CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4601 strSQL = PrepareSQL("UPDATE movie SET c%02d='%s' WHERE idMovie=%i", VIDEODB_ID_TITLE, strNewMovieTitle.c_str(), idMovie );
4604 else if (iType == VIDEODB_CONTENT_EPISODES)
4606 CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4607 strSQL = PrepareSQL("UPDATE episode SET c%02d='%s' WHERE idEpisode=%i", VIDEODB_ID_EPISODE_TITLE, strNewMovieTitle.c_str(), idMovie );
4608 content = "episode";
4610 else if (iType == VIDEODB_CONTENT_TVSHOWS)
4612 CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4613 strSQL = PrepareSQL("UPDATE tvshow SET c%02d='%s' WHERE idShow=%i", VIDEODB_ID_TV_TITLE, strNewMovieTitle.c_str(), idMovie );
4616 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4618 CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4619 strSQL = PrepareSQL("UPDATE musicvideo SET c%02d='%s' WHERE idMVideo=%i", VIDEODB_ID_MUSICVIDEO_TITLE, strNewMovieTitle.c_str(), idMovie );
4620 content = "musicvideo";
4622 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4624 CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4625 strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4627 m_pDS->exec(strSQL.c_str());
4629 if (content.size() > 0)
4630 AnnounceUpdate(content, idMovie);
4634 CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4638 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4639 void CVideoDatabase::EraseVideoSettings()
4643 CLog::Log(LOGINFO, "Deleting settings information for all movies");
4644 m_pDS->exec("delete from settings");
4648 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4652 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4654 return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4657 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4659 return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4662 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4664 return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4667 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4671 if (NULL == m_pDB.get()) return false;
4672 if (NULL == m_pDS.get()) return false;
4675 Filter extFilter = filter;
4676 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4678 if (idContent == VIDEODB_CONTENT_MOVIES)
4680 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4681 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());
4682 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",
4683 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4685 else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4687 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4688 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4689 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",
4690 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4692 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4694 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4695 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());
4696 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",
4697 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4704 if (idContent == VIDEODB_CONTENT_MOVIES)
4706 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4707 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());
4708 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",
4709 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4710 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4712 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4714 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4715 extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4716 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4717 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4719 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4721 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4722 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());
4723 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",
4724 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4725 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4733 extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4734 extFilter.group.clear();
4735 extFilter.order.clear();
4737 strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4739 CVideoDbUrl videoUrl;
4740 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4743 int iRowsFound = RunQuery(strSQL);
4744 if (iRowsFound <= 0)
4745 return iRowsFound == 0;
4749 CFileItemPtr pItem(new CFileItem());
4750 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4757 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4759 map<int, pair<CStdString,int> > mapItems;
4760 map<int, pair<CStdString,int> >::iterator it;
4761 while (!m_pDS->eof())
4763 int id = m_pDS->fv(0).get_asInt();
4764 CStdString str = m_pDS->fv(1).get_asString();
4766 // was this already found?
4767 it = mapItems.find(id);
4768 if (it == mapItems.end())
4771 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),g_settings.m_videoSources))
4773 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4774 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
4775 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4776 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
4783 for (it = mapItems.begin(); it != mapItems.end(); ++it)
4785 CFileItemPtr pItem(new CFileItem(it->second.first));
4786 pItem->GetVideoInfoTag()->m_iDbId = it->first;
4787 pItem->GetVideoInfoTag()->m_type = type;
4789 CVideoDbUrl itemUrl = videoUrl;
4790 CStdString path; path.Format("%ld/", it->first);
4791 itemUrl.AppendPath(path);
4792 pItem->SetPath(itemUrl.ToString());
4794 pItem->m_bIsFolder = true;
4795 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4796 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
4797 if (!items.Contains(pItem->GetPath()))
4799 pItem->SetLabelPreformated(true);
4806 while (!m_pDS->eof())
4808 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4809 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
4810 pItem->GetVideoInfoTag()->m_type = type;
4812 CVideoDbUrl itemUrl = videoUrl;
4813 CStdString path; path.Format("%ld/", m_pDS->fv(0).get_asInt());
4814 itemUrl.AppendPath(path);
4815 pItem->SetPath(itemUrl.ToString());
4817 pItem->m_bIsFolder = true;
4818 pItem->SetLabelPreformated(true);
4819 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4820 { // fv(3) is the number of videos watched, fv(2) is the total number. We set the playcount
4821 // only if the number of videos watched is equal to the total number (i.e. every video watched)
4822 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
4833 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4838 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4840 CStdString mediaType;
4841 if (idContent == VIDEODB_CONTENT_MOVIES)
4842 mediaType = "movie";
4843 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4844 mediaType = "tvshow";
4845 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4846 mediaType = "musicvideo";
4852 if (NULL == m_pDB.get()) return false;
4853 if (NULL == m_pDS.get()) return false;
4855 CStdString strSQL = "SELECT %s FROM taglinks ";
4857 Filter extFilter = filter;
4858 extFilter.fields = "tag.idTag, tag.strTag";
4859 extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
4861 if (idContent == (int)VIDEODB_CONTENT_MOVIES)
4862 extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
4864 extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
4865 extFilter.AppendGroup("taglinks.idTag");
4869 extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
4870 extFilter.group.clear();
4871 extFilter.order.clear();
4873 strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4875 // parse the base path to get additional filters
4876 CVideoDbUrl videoUrl;
4877 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4880 int iRowsFound = RunQuery(strSQL);
4881 if (iRowsFound <= 0)
4882 return iRowsFound == 0;
4886 CFileItemPtr pItem(new CFileItem());
4887 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4894 while (!m_pDS->eof())
4896 int idTag = m_pDS->fv(0).get_asInt();
4898 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4899 pItem->m_bIsFolder = true;
4900 pItem->GetVideoInfoTag()->m_iDbId = idTag;
4901 pItem->GetVideoInfoTag()->m_type = "tag";
4903 CVideoDbUrl itemUrl = videoUrl;
4904 CStdString path; path.Format("%ld/", idTag);
4905 itemUrl.AppendPath(path);
4906 pItem->SetPath(itemUrl.ToString());
4908 if (!items.Contains(pItem->GetPath()))
4910 pItem->SetLabelPreformated(true);
4922 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4927 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent, bool ignoreSingleMovieSets /* = false */)
4929 if (idContent != VIDEODB_CONTENT_MOVIES)
4933 return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
4936 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
4940 if (NULL == m_pDB.get()) return false;
4941 if (NULL == m_pDS.get()) return false;
4943 CVideoDbUrl videoUrl;
4944 if (!videoUrl.FromString(strBaseDir))
4947 Filter setFilter = filter;
4948 setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
4949 if (!setFilter.order.empty())
4950 setFilter.order += ",";
4951 setFilter.order += "sets.idSet";
4953 if (!GetMoviesByWhere(strBaseDir, setFilter, items))
4957 if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
4967 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4972 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4976 if (NULL == m_pDB.get()) return false;
4977 if (NULL == m_pDS.get()) return false;
4979 CStdString strSQL = "select %s from musicvideoview ";
4980 Filter extFilter = filter;
4981 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4983 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor, path.strPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
4984 extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath");
4988 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
4989 extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist");
4992 extFilter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", idArtist));
4994 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
4998 extFilter.fields = "COUNT(1)";
4999 extFilter.group.clear();
5000 extFilter.order.clear();
5002 strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5004 CVideoDbUrl videoUrl;
5005 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5008 int iRowsFound = RunQuery(strSQL);
5009 if (iRowsFound <= 0)
5010 return iRowsFound == 0;
5014 CFileItemPtr pItem(new CFileItem());
5015 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5022 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5024 map<int, pair<CStdString,CStdString> > mapAlbums;
5025 map<int, pair<CStdString,CStdString> >::iterator it;
5026 while (!m_pDS->eof())
5028 int lidMVideo = m_pDS->fv(1).get_asInt();
5029 CStdString strAlbum = m_pDS->fv(0).get_asString();
5030 it = mapAlbums.find(lidMVideo);
5031 // was this genre already found?
5032 if (it == mapAlbums.end())
5035 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5036 mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5042 for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5044 if (!it->second.first.IsEmpty())
5046 CFileItemPtr pItem(new CFileItem(it->second.first));
5048 CVideoDbUrl itemUrl = videoUrl;
5049 CStdString path; path.Format("%ld/", it->first);
5050 itemUrl.AppendPath(path);
5051 pItem->SetPath(itemUrl.ToString());
5053 pItem->m_bIsFolder=true;
5054 pItem->SetLabelPreformated(true);
5055 if (!items.Contains(pItem->GetPath()))
5057 pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5065 while (!m_pDS->eof())
5067 if (!m_pDS->fv(0).get_asString().empty())
5069 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5071 CVideoDbUrl itemUrl = videoUrl;
5072 CStdString path; path.Format("%ld/", m_pDS->fv(1).get_asInt());
5073 itemUrl.AppendPath(path);
5074 pItem->SetPath(itemUrl.ToString());
5076 pItem->m_bIsFolder=true;
5077 pItem->SetLabelPreformated(true);
5078 if (!items.Contains(pItem->GetPath()))
5080 pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5089 // CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5094 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5099 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5101 return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5104 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5106 return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5109 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5111 if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5112 { // set thumbs - ideally this should be in the normal thumb setting routines
5113 for (int i = 0; i < items.Size() && !countOnly; i++)
5115 CFileItemPtr pItem = items[i];
5116 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5117 pItem->SetIconImage("DefaultArtist.png");
5119 pItem->SetIconImage("DefaultActor.png");
5126 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5128 if (NULL == m_pDB.get()) return false;
5129 if (NULL == m_pDS.get()) return false;
5133 // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5134 // point. This means that we *MUST* filter these levels as you'll get double ups. Ideally we'd allow playcount
5135 // to filter through as we normally do for tvshows to save this happening.
5136 // Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5137 // Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5140 // General routine that the other actor/director/writer routines call
5142 // get primary genres for movies
5144 Filter extFilter = filter;
5145 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5147 if (idContent == VIDEODB_CONTENT_MOVIES)
5149 strSQL = "select %s from actors ";
5150 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5151 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on actors.idActor = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile join path on path.idPath = files.idPath",
5152 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5154 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5156 strSQL = "select %s from actors ";
5157 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5158 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",
5159 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5161 else if (idContent == VIDEODB_CONTENT_EPISODES)
5163 strSQL = "select %s from actors ";
5164 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5165 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",
5166 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5168 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5170 strSQL = "select %s from actors ";
5171 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5172 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",
5173 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5180 if (idContent == VIDEODB_CONTENT_MOVIES)
5182 strSQL ="select %s from actors ";
5183 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5184 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",
5185 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5186 extFilter.AppendGroup("actors.idActor");
5188 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5190 strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5191 extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5192 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5193 type.c_str(), type.c_str(), type.c_str()));
5195 else if (idContent == VIDEODB_CONTENT_EPISODES)
5197 strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5198 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5199 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5200 type.c_str(), type.c_str(), type.c_str()));
5201 extFilter.AppendGroup("actors.idActor");
5203 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5205 strSQL = "select %s from actors ";
5206 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5207 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",
5208 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5209 extFilter.AppendGroup("actors.idActor");
5217 extFilter.fields = "COUNT(1)";
5218 extFilter.group.clear();
5219 extFilter.order.clear();
5221 strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5223 CVideoDbUrl videoUrl;
5224 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5228 unsigned int time = XbmcThreads::SystemClockMillis();
5229 if (!m_pDS->query(strSQL.c_str())) return false;
5230 CLog::Log(LOGDEBUG, "%s - query took %i ms",
5231 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5232 int iRowsFound = m_pDS->num_rows();
5233 if (iRowsFound == 0)
5241 CFileItemPtr pItem(new CFileItem());
5242 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5249 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5251 map<int, CActor> mapActors;
5252 map<int, CActor>::iterator it;
5254 while (!m_pDS->eof())
5256 int idActor = m_pDS->fv(0).get_asInt();
5258 actor.name = m_pDS->fv(1).get_asString();
5259 actor.thumb = m_pDS->fv(2).get_asString();
5260 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5261 actor.playcount = m_pDS->fv(3).get_asInt();
5262 it = mapActors.find(idActor);
5263 // is this actor already known?
5264 if (it == mapActors.end())
5267 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5268 mapActors.insert(pair<int, CActor>(idActor, actor));
5274 for (it=mapActors.begin();it != mapActors.end();++it)
5276 CFileItemPtr pItem(new CFileItem(it->second.name));
5278 CVideoDbUrl itemUrl = videoUrl;
5279 CStdString path; path.Format("%ld/", it->first);
5280 itemUrl.AppendPath(path);
5281 pItem->SetPath(itemUrl.ToString());
5283 pItem->m_bIsFolder=true;
5284 pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5285 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5286 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5287 pItem->GetVideoInfoTag()->m_type = type;
5293 while (!m_pDS->eof())
5297 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5299 CVideoDbUrl itemUrl = videoUrl;
5300 CStdString path; path.Format("%ld/", m_pDS->fv(0).get_asInt());
5301 itemUrl.AppendPath(path);
5302 pItem->SetPath(itemUrl.ToString());
5304 pItem->m_bIsFolder=true;
5305 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5306 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5307 pItem->GetVideoInfoTag()->m_type = type;
5308 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5310 // fv(4) is the number of videos watched, fv(3) is the total number. We set the playcount
5311 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5312 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5314 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5315 pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5322 CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5323 return items.Size() > 0;
5328 CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5329 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5336 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5341 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5345 if (NULL == m_pDB.get()) return false;
5346 if (NULL == m_pDS.get()) return false;
5349 Filter extFilter = filter;
5350 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5352 if (idContent == VIDEODB_CONTENT_MOVIES)
5354 strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5355 extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5357 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5359 strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5360 extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5362 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5364 strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5365 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5373 if (idContent == VIDEODB_CONTENT_MOVIES)
5375 strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5376 extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5377 extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5379 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5380 strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5381 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5383 strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5384 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5385 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5391 CVideoDbUrl videoUrl;
5392 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5395 int iRowsFound = RunQuery(strSQL);
5396 if (iRowsFound <= 0)
5397 return iRowsFound == 0;
5399 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5401 map<int, pair<CStdString,int> > mapYears;
5402 map<int, pair<CStdString,int> >::iterator it;
5403 while (!m_pDS->eof())
5406 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5409 time.SetFromDateString(m_pDS->fv(0).get_asString());
5410 lYear = time.GetYear();
5412 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5413 lYear = m_pDS->fv(0).get_asInt();
5414 it = mapYears.find(lYear);
5415 if (it == mapYears.end())
5418 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5421 year.Format("%d", lYear);
5422 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5423 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5425 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5432 for (it=mapYears.begin();it != mapYears.end();++it)
5436 CFileItemPtr pItem(new CFileItem(it->second.first));
5438 CVideoDbUrl itemUrl = videoUrl;
5439 CStdString path; path.Format("%ld/", it->first);
5440 itemUrl.AppendPath(path);
5441 pItem->SetPath(itemUrl.ToString());
5443 pItem->m_bIsFolder=true;
5444 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5445 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5451 while (!m_pDS->eof())
5454 CStdString strLabel;
5455 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5458 time.SetFromDateString(m_pDS->fv(0).get_asString());
5459 lYear = time.GetYear();
5460 strLabel.Format("%i",lYear);
5462 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5464 lYear = m_pDS->fv(0).get_asInt();
5465 strLabel = m_pDS->fv(0).get_asString();
5472 CFileItemPtr pItem(new CFileItem(strLabel));
5474 CVideoDbUrl itemUrl = videoUrl;
5475 CStdString path; path.Format("%ld/", lYear);
5476 itemUrl.AppendPath(path);
5477 pItem->SetPath(itemUrl.ToString());
5479 pItem->m_bIsFolder=true;
5480 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5482 // fv(2) is the number of videos watched, fv(1) is the total number. We set the playcount
5483 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5484 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5487 // take care of dupes ..
5488 if (!items.Contains(pItem->GetPath()))
5500 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5505 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5509 if (NULL == m_pDB.get()) return false;
5510 if (NULL == m_pDS.get()) return false;
5512 // look for duplicate show titles and stack them into a list
5515 CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5516 CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5517 if (!m_pDS->query(strSQL.c_str())) return false;
5518 int iRows = m_pDS->num_rows();
5519 if (iRows == 0) return false; // this should never happen!
5521 { // more than one show, so stack them up
5523 while (!m_pDS->eof())
5525 strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5528 strIn[strIn.GetLength() - 1] = ')'; // replace last , with )
5535 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5540 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5544 if (NULL == m_pDB.get()) return false;
5545 if (NULL == m_pDS.get()) return false;
5547 // parse the base path to get additional filters
5548 CVideoDbUrl videoUrl;
5549 if (!videoUrl.FromString(strBaseDir))
5552 CStdString strIn = PrepareSQL("= %i", idShow);
5553 GetStackedTvShowList(idShow, strIn);
5555 CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5557 "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5558 "seasons.idSeason, "
5559 "count(1), count(files.playCount) "
5560 "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);
5563 filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5564 "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5565 "JOIN files ON files.idFile = episodeview.idFile "
5566 "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5567 "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5568 filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5569 filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5571 videoUrl.AddOption("tvshowid", idShow);
5574 videoUrl.AddOption("actorid", idActor);
5575 else if (idDirector != -1)
5576 videoUrl.AddOption("directorid", idDirector);
5577 else if (idGenre != -1)
5578 videoUrl.AddOption("genreid", idGenre);
5579 else if (idYear != -1)
5580 videoUrl.AddOption("year", idYear);
5582 if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5585 int iRowsFound = RunQuery(strSQL);
5586 if (iRowsFound <= 0)
5587 return iRowsFound == 0;
5589 // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5590 CStdString showTitle = m_pDS->fv(2).get_asString();
5591 CStdString showPlot = m_pDS->fv(3).get_asString();
5592 CStdString showPremiered = m_pDS->fv(4).get_asString();
5593 CStdString showStudio = m_pDS->fv(6).get_asString();
5594 CStdString showMPAARating = m_pDS->fv(7).get_asString();
5596 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5598 map<int, CSeason> mapSeasons;
5599 map<int, CSeason>::iterator it;
5600 while (!m_pDS->eof())
5602 int iSeason = m_pDS->fv(0).get_asInt();
5603 it = mapSeasons.find(iSeason);
5605 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5610 if (it == mapSeasons.end())
5613 season.path = m_pDS->fv(1).get_asString();
5614 season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5615 season.id = m_pDS->fv(8).get_asInt();
5616 season.numEpisodes = m_pDS->fv(9).get_asInt();
5617 season.numWatched = m_pDS->fv(10).get_asInt();
5618 mapSeasons.insert(make_pair(iSeason, season));
5624 for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5626 int iSeason = it->first;
5627 CStdString strLabel;
5629 strLabel = g_localizeStrings.Get(20381);
5631 strLabel.Format(g_localizeStrings.Get(20358),iSeason);
5632 CFileItemPtr pItem(new CFileItem(strLabel));
5634 CVideoDbUrl itemUrl = videoUrl;
5635 CStdString strDir; strDir.Format("%ld/", it->first);
5636 itemUrl.AppendPath(strDir);
5637 pItem->SetPath(itemUrl.ToString());
5639 pItem->m_bIsFolder=true;
5640 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5641 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5642 pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5643 pItem->GetVideoInfoTag()->m_type = "season";
5644 pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5645 pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5646 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5647 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5648 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5649 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5650 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5651 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5652 pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5653 pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5654 pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5655 pItem->SetProperty("watchedepisodes", it->second.numWatched);
5656 pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5657 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5658 pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5659 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5665 while (!m_pDS->eof())
5667 int iSeason = m_pDS->fv(0).get_asInt();
5668 CStdString strLabel;
5670 strLabel = g_localizeStrings.Get(20381);
5672 strLabel.Format(g_localizeStrings.Get(20358),iSeason);
5673 CFileItemPtr pItem(new CFileItem(strLabel));
5675 CVideoDbUrl itemUrl = videoUrl;
5676 CStdString strDir; strDir.Format("%ld/", iSeason);
5677 itemUrl.AppendPath(strDir);
5678 pItem->SetPath(itemUrl.ToString());
5680 pItem->m_bIsFolder=true;
5681 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5682 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5683 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5684 pItem->GetVideoInfoTag()->m_type = "season";
5685 pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5686 pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5687 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5688 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5689 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5690 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5691 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5692 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5693 int totalEpisodes = m_pDS->fv(9).get_asInt();
5694 int watchedEpisodes = m_pDS->fv(10).get_asInt();
5695 pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5696 pItem->SetProperty("totalepisodes", totalEpisodes);
5697 pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5698 pItem->SetProperty("watchedepisodes", watchedEpisodes);
5699 pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5700 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5701 pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5702 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5709 // now add any linked movies
5710 if (getLinkedMovies)
5713 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5714 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5715 CFileItemList movieItems;
5716 GetMoviesByWhere("videodb://1/2/", movieFilter, movieItems);
5718 if (movieItems.Size() > 0)
5719 items.Append(movieItems);
5726 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5731 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5733 if (NULL == m_pDB.get() || NULL == m_pDS.get())
5736 if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5739 SortDescription sorting = sortDescription;
5740 if (sortDescription.sortBy == SortByFile ||
5741 sortDescription.sortBy == SortByTitle ||
5742 sortDescription.sortBy == SortBySortTitle ||
5743 sortDescription.sortBy == SortByLabel ||
5744 sortDescription.sortBy == SortByDateAdded ||
5745 sortDescription.sortBy == SortByRating ||
5746 sortDescription.sortBy == SortByYear ||
5747 sortDescription.sortBy == SortByLastPlayed ||
5748 sortDescription.sortBy == SortByPlaycount)
5749 sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5751 bool success = false;
5754 case MediaTypeMovie:
5755 success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5758 case MediaTypeTvShow:
5759 success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
5762 case MediaTypeEpisode:
5763 success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
5766 case MediaTypeMusicVideo:
5767 success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
5774 items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
5778 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
5779 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
5780 int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
5781 const SortDescription &sortDescription /* = SortDescription() */)
5783 CVideoDbUrl videoUrl;
5784 if (!videoUrl.FromString(strBaseDir))
5788 videoUrl.AddOption("genreid", idGenre);
5789 else if (idCountry > 0)
5790 videoUrl.AddOption("countryid", idCountry);
5791 else if (idStudio > 0)
5792 videoUrl.AddOption("studioid", idStudio);
5793 else if (idDirector > 0)
5794 videoUrl.AddOption("directorid", idDirector);
5795 else if (idYear > 0)
5796 videoUrl.AddOption("year", idYear);
5797 else if (idActor > 0)
5798 videoUrl.AddOption("actorid", idActor);
5800 videoUrl.AddOption("setid", idSet);
5802 videoUrl.AddOption("tagid", idTag);
5805 return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
5808 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5815 if (NULL == m_pDB.get()) return false;
5816 if (NULL == m_pDS.get()) return false;
5818 // parse the base path to get additional filters
5819 CVideoDbUrl videoUrl;
5820 Filter extFilter = filter;
5821 SortDescription sorting = sortDescription;
5822 if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
5827 CStdString strSQL = "select %s from movieview ";
5828 CStdString strSQLExtra;
5829 if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
5832 // Apply the limiting directly here if there's no special sorting but limiting
5833 if (extFilter.limit.empty() &&
5834 sorting.sortBy == SortByNone &&
5835 (sorting.limitStart > 0 || sorting.limitEnd > 0))
5837 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
5838 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
5841 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
5843 int iRowsFound = RunQuery(strSQL);
5844 if (iRowsFound <= 0)
5845 return iRowsFound == 0;
5847 // store the total value of items as a property
5848 if (total < iRowsFound)
5850 items.SetProperty("total", total);
5852 DatabaseResults results;
5853 results.reserve(iRowsFound);
5855 if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
5858 // get data from returned rows
5859 items.Reserve(results.size());
5860 const query_data &data = m_pDS->get_result_set().records;
5861 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
5863 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
5864 const dbiplus::sql_record* const record = data.at(targetRow);
5866 CVideoInfoTag movie = GetDetailsForMovie(record);
5867 if (g_settings.GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
5868 g_passwordManager.bMasterUser ||
5869 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, g_settings.m_videoSources))
5871 CFileItemPtr pItem(new CFileItem(movie));
5873 CVideoDbUrl itemUrl = videoUrl;
5874 CStdString path; path.Format("%ld", movie.m_iDbId);
5875 itemUrl.AppendPath(path);
5876 pItem->SetPath(itemUrl.ToString());
5878 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
5889 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5894 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
5895 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
5896 const SortDescription &sortDescription /* = SortDescription() */)
5898 CVideoDbUrl videoUrl;
5899 if (!videoUrl.FromString(strBaseDir))
5903 videoUrl.AddOption("genreid", idGenre);
5904 else if (idStudio != -1)
5905 videoUrl.AddOption("studioid", idStudio);
5906 else if (idDirector != -1)
5907 videoUrl.AddOption("directorid", idDirector);
5908 else if (idYear != -1)
5909 videoUrl.AddOption("year", idYear);
5910 else if (idActor != -1)
5911 videoUrl.AddOption("actorid", idActor);
5912 else if (idTag != -1)
5913 videoUrl.AddOption("tagid", idTag);
5916 return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
5919 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5925 if (NULL == m_pDB.get()) return false;
5926 if (NULL == m_pDS.get()) return false;
5930 CStdString strSQL = "SELECT %s FROM tvshowview ";
5931 CVideoDbUrl videoUrl;
5932 CStdString strSQLExtra;
5933 Filter extFilter = filter;
5934 SortDescription sorting = sortDescription;
5935 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
5938 // Apply the limiting directly here if there's no special sorting but limiting
5939 if (extFilter.limit.empty() &&
5940 sorting.sortBy == SortByNone &&
5941 (sorting.limitStart > 0 || sorting.limitEnd > 0))
5943 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
5944 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
5947 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
5949 int iRowsFound = RunQuery(strSQL);
5950 if (iRowsFound <= 0)
5951 return iRowsFound == 0;
5953 // store the total value of items as a property
5954 if (total < iRowsFound)
5956 items.SetProperty("total", total);
5958 DatabaseResults results;
5959 results.reserve(iRowsFound);
5960 if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
5963 // get data from returned rows
5964 items.Reserve(results.size());
5965 const query_data &data = m_pDS->get_result_set().records;
5966 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
5968 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
5969 const dbiplus::sql_record* const record = data.at(targetRow);
5971 CVideoInfoTag movie = GetDetailsForTvShow(record, false);
5972 if ((g_settings.GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
5973 g_passwordManager.bMasterUser ||
5974 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, g_settings.m_videoSources)) &&
5975 (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
5977 CFileItemPtr pItem(new CFileItem(movie));
5979 CVideoDbUrl itemUrl = videoUrl;
5980 CStdString path; path.Format("%ld/", record->at(0).get_asInt());
5981 itemUrl.AppendPath(path);
5982 pItem->SetPath(itemUrl.ToString());
5984 pItem->m_dateTime = movie.m_premiered;
5985 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
5986 pItem->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
5987 pItem->SetProperty("totalepisodes", movie.m_iEpisode);
5988 pItem->SetProperty("numepisodes", movie.m_iEpisode); // will be changed later to reflect watchmode setting
5989 pItem->SetProperty("watchedepisodes", movie.m_playCount);
5990 pItem->SetProperty("unwatchedepisodes", movie.m_iEpisode - movie.m_playCount);
5991 pItem->GetVideoInfoTag()->m_playCount = (movie.m_iEpisode == movie.m_playCount) ? 1 : 0;
5992 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5997 Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6005 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6010 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6012 if (maintainSortOrder)
6014 // save current sort order
6015 for (int i = 0; i < items.Size(); i++)
6016 items[i]->m_iprogramCount = i;
6021 case VIDEODB_CONTENT_TVSHOWS:
6024 items.Sort(SORT_METHOD_VIDEO_TITLE, SortOrderAscending);
6027 while (i < items.Size())
6029 CFileItemPtr pItem = items.Get(i);
6030 CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6031 CStdString strFanArt = pItem->GetArt("fanart");
6034 bool bStacked = false;
6035 while (j < items.Size())
6037 CFileItemPtr jItem = items.Get(j);
6039 // matching title? append information
6040 if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6042 if (jItem->GetVideoInfoTag()->m_premiered !=
6043 pItem->GetVideoInfoTag()->m_premiered)
6050 // increment episode counts
6051 pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6052 pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6053 pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6054 pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6055 pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6057 // adjust lastplayed
6058 if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6059 pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6061 // check for fanart if not already set
6062 if (strFanArt.IsEmpty())
6063 strFanArt = jItem->GetArt("fanart");
6065 // remove duplicate entry
6068 // no match? exit loop
6072 // update playcount and fanart
6075 pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6076 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6077 if (!strFanArt.IsEmpty())
6078 pItem->SetArt("fanart", strFanArt);
6080 // increment i to j which is the next item
6085 // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6086 // so that if we eventually want to stack during scan we can utilize it.
6088 case VIDEODB_CONTENT_EPISODES:
6090 // sort by ShowTitle, Episode, Filename
6091 items.Sort(SORT_METHOD_EPISODE, SortOrderAscending);
6094 while (i < items.Size())
6096 CFileItemPtr pItem = items.Get(i);
6097 CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6098 int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6099 int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6100 //CStdString strFanArt = pItem->GetArt("fanart");
6102 // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6103 CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6104 bool bDvdFolder = strFileNameAndPath.Right(12).Equals("VIDEO_TS.IFO");
6106 vector<CStdString> paths;
6107 paths.push_back(strFileNameAndPath);
6108 CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6111 int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6112 while (j < items.Size())
6114 CFileItemPtr jItem = items.Get(j);
6115 const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6116 CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6118 CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6119 // compare path, season, episode
6122 jTag->m_strPath.Equals(strPath) &&
6123 jTag->m_iSeason == iSeason &&
6124 jTag->m_iEpisode == iEpisode
6127 // keep checking to see if this is dvd folder
6130 bDvdFolder = jFileNameAndPath.Right(12).Equals("VIDEO_TS.IFO");
6131 // if we have a dvd folder, we stack differently
6134 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6136 paths.push_back(jFileNameAndPath);
6140 // increment playcount
6141 iPlayCount += jTag->m_playCount;
6143 // episodes dont have fanart yet
6144 //if (strFanArt.IsEmpty())
6145 // strFanArt = jItem->GetArt("fanart");
6147 paths.push_back(jFileNameAndPath);
6151 // remove duplicate entry
6155 // no match? exit loop
6159 // update playcount and fanart if we have a stacked entry
6160 if (paths.size() > 1)
6162 CStackDirectory dir;
6163 CStdString strStack;
6164 dir.ConstructStackPath(paths, strStack);
6165 pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6166 pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6167 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6169 // episodes dont have fanart yet
6170 //if (!strFanArt.IsEmpty())
6171 // pItem->SetArt("fanart", strFanArt);
6173 // increment i to j which is the next item
6179 // stack other types later
6183 if (maintainSortOrder)
6185 // restore original sort order - essential for smartplaylists
6186 items.Sort(SORT_METHOD_PROGRAM_COUNT, SortOrderAscending);
6190 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6192 CVideoDbUrl videoUrl;
6193 if (!videoUrl.FromString(strBaseDir))
6199 strIn = PrepareSQL("= %i", idShow);
6200 GetStackedTvShowList(idShow, strIn);
6202 videoUrl.AddOption("tvshowid", idShow);
6204 videoUrl.AddOption("season", idSeason);
6207 videoUrl.AddOption("genreid", idGenre);
6208 else if (idYear !=-1)
6209 videoUrl.AddOption("year", idYear);
6210 else if (idActor != -1)
6211 videoUrl.AddOption("actorid", idActor);
6213 else if (idYear != -1)
6214 videoUrl.AddOption("year", idYear);
6216 if (idDirector != -1)
6217 videoUrl.AddOption("directorid", idDirector);
6220 bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6222 if (idSeason == -1 && idShow != -1)
6223 { // add any linked movies
6225 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6226 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6227 CFileItemList movieItems;
6228 GetMoviesByWhere("videodb://1/2/", movieFilter, movieItems);
6230 if (movieItems.Size() > 0)
6231 items.Append(movieItems);
6237 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6244 if (NULL == m_pDB.get()) return false;
6245 if (NULL == m_pDS.get()) return false;
6249 CStdString strSQL = "select %s from episodeview ";
6250 CVideoDbUrl videoUrl;
6251 CStdString strSQLExtra;
6252 Filter extFilter = filter;
6253 SortDescription sorting = sortDescription;
6254 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6257 // Apply the limiting directly here if there's no special sorting but limiting
6258 if (extFilter.limit.empty() &&
6259 sorting.sortBy == SortByNone &&
6260 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6262 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6263 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6266 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6268 int iRowsFound = RunQuery(strSQL);
6269 if (iRowsFound <= 0)
6270 return iRowsFound == 0;
6272 // store the total value of items as a property
6273 if (total < iRowsFound)
6275 items.SetProperty("total", total);
6277 DatabaseResults results;
6278 results.reserve(iRowsFound);
6279 if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6282 // get data from returned rows
6283 items.Reserve(results.size());
6284 CLabelFormatter formatter("%H. %T", "");
6286 const query_data &data = m_pDS->get_result_set().records;
6287 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6289 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6290 const dbiplus::sql_record* const record = data.at(targetRow);
6292 CVideoInfoTag movie = GetDetailsForEpisode(record);
6293 if (g_settings.GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6294 g_passwordManager.bMasterUser ||
6295 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, g_settings.m_videoSources))
6297 CFileItemPtr pItem(new CFileItem(movie));
6298 formatter.FormatLabel(pItem.get());
6300 int idEpisode = record->at(0).get_asInt();
6302 CVideoDbUrl itemUrl = videoUrl;
6304 if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6305 path.Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6307 path.Format("%ld", idEpisode);
6308 itemUrl.AppendPath(path);
6309 pItem->SetPath(itemUrl.ToString());
6311 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6312 pItem->m_dateTime = movie.m_firstAired;
6313 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6324 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6329 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() */)
6331 CVideoDbUrl videoUrl;
6332 if (!videoUrl.FromString(strBaseDir))
6336 videoUrl.AddOption("genreid", idGenre);
6337 else if (idStudio != -1)
6338 videoUrl.AddOption("studioid", idStudio);
6339 else if (idDirector != -1)
6340 videoUrl.AddOption("directorid", idDirector);
6341 else if (idYear !=-1)
6342 videoUrl.AddOption("year", idYear);
6343 else if (idArtist != -1)
6344 videoUrl.AddOption("artistid", idArtist);
6345 else if (idTag != -1)
6346 videoUrl.AddOption("tagid", idTag);
6348 videoUrl.AddOption("albumid", idAlbum);
6351 return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6354 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6357 filter.order = "dateAdded desc, idMovie desc";
6358 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6359 return GetMoviesByWhere(strBaseDir, filter, items);
6362 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6365 filter.order = "dateAdded desc, idEpisode desc";
6366 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6367 return GetEpisodesByWhere(strBaseDir, filter, items, false);
6370 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6373 filter.order = "dateAdded desc, idMVideo desc";
6374 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6375 return GetMusicVideosByWhere(strBaseDir, filter, items);
6378 CStdString CVideoDatabase::GetGenreById(int id)
6380 return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6383 CStdString CVideoDatabase::GetCountryById(int id)
6385 return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6388 CStdString CVideoDatabase::GetSetById(int id)
6390 return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6393 CStdString CVideoDatabase::GetTagById(int id)
6395 return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6398 CStdString CVideoDatabase::GetPersonById(int id)
6400 return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6403 CStdString CVideoDatabase::GetStudioById(int id)
6405 return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6408 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6410 return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6413 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6415 return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6418 bool CVideoDatabase::HasSets() const
6422 if (NULL == m_pDB.get()) return false;
6423 if (NULL == m_pDS.get()) return false;
6425 m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6426 "JOIN sets ON sets.idSet = movieview.idSet "
6427 "GROUP BY movieview.idSet HAVING c>1");
6429 bool bResult = (m_pDS->num_rows() > 0);
6435 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6440 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6444 if (NULL == m_pDB.get()) return false;
6445 if (NULL == m_pDS2.get()) return false;
6447 // make sure we use m_pDS2, as this is called in loops using m_pDS
6448 CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6449 m_pDS2->query( strSQL.c_str() );
6453 result=m_pDS2->fv(0).get_asInt();
6460 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6465 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6468 sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6469 CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6472 return atoi(id.c_str());
6475 bool CVideoDatabase::HasContent()
6477 return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6478 HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6479 HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6482 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6484 bool result = false;
6487 if (NULL == m_pDB.get()) return false;
6488 if (NULL == m_pDS.get()) return false;
6491 if (type == VIDEODB_CONTENT_MOVIES)
6492 sql = "select count(1) from movie";
6493 else if (type == VIDEODB_CONTENT_TVSHOWS)
6494 sql = "select count(1) from tvshow";
6495 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6496 sql = "select count(1) from musicvideo";
6497 m_pDS->query( sql.c_str() );
6500 result = (m_pDS->fv(0).get_asInt() > 0);
6506 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6511 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6515 if (NULL == m_pDB.get()) return 0;
6516 if (NULL == m_pDS.get()) return 0;
6519 strSQL.Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6520 m_pDS->query( strSQL.c_str() );
6524 iResult = m_pDS->fv("nummovies").get_asInt();
6531 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6536 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6538 SScanSettings settings;
6539 return GetScraperForPath(strPath, settings);
6542 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6545 return GetScraperForPath(strPath, settings, dummy);
6548 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6550 foundDirectly = false;
6553 if (strPath.IsEmpty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6556 CStdString strPath1;
6557 CStdString strPath2(strPath);
6559 if (URIUtils::IsMultiPath(strPath))
6560 strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6562 URIUtils::GetDirectory(strPath2,strPath1);
6563 int idPath = GetPathId(strPath1);
6567 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);
6568 m_pDS->query( strSQL.c_str() );
6572 CONTENT_TYPE content = CONTENT_NONE;
6574 { // path is stored in db
6576 if (m_pDS->fv("path.exclude").get_asBool())
6578 settings.exclude = true;
6580 return ScraperPtr();
6582 settings.exclude = false;
6584 // try and ascertain scraper for this path
6585 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6586 strcontent.ToLower();
6587 content = TranslateContent(strcontent);
6589 //FIXME paths stored should not have empty strContent
6590 //assert(content != CONTENT_NONE);
6591 CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6594 if (!scraperID.empty() &&
6595 CAddonMgr::Get().GetAddon(scraperID, addon))
6597 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone(addon));
6599 return ScraperPtr();
6601 // store this path's content & settings
6602 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6603 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6604 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6605 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6609 if (content == CONTENT_NONE)
6610 { // this path is not saved in db
6611 // we must drill up until a scraper is configured
6612 CStdString strParent;
6613 while (URIUtils::GetParentPath(strPath1, strParent))
6617 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());
6618 m_pDS->query(strSQL.c_str());
6620 CONTENT_TYPE content = CONTENT_NONE;
6624 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6625 strcontent.ToLower();
6626 if (m_pDS->fv("path.exclude").get_asBool())
6628 settings.exclude = true;
6634 content = TranslateContent(strcontent);
6637 if (content != CONTENT_NONE &&
6638 CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6640 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone(addon));
6641 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6642 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6643 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6644 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6645 settings.exclude = false;
6649 strPath1 = strParent;
6654 if (!scraper || scraper->Content() == CONTENT_NONE)
6655 return ScraperPtr();
6657 if (scraper->Content() == CONTENT_TVSHOWS)
6659 settings.recurse = 0;
6660 if(settings.parent_name) // single show
6662 settings.parent_name_root = settings.parent_name = (iFound == 1);
6666 settings.parent_name_root = settings.parent_name = (iFound == 2);
6669 else if (scraper->Content() == CONTENT_MOVIES)
6671 settings.recurse = settings.recurse - (iFound-1);
6672 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6674 else if (scraper->Content() == CONTENT_MUSICVIDEOS)
6676 settings.recurse = settings.recurse - (iFound-1);
6677 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6682 return ScraperPtr();
6684 foundDirectly = (iFound == 1);
6689 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6691 return ScraperPtr();
6694 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
6696 SScanSettings settings;
6697 bool foundDirectly = false;
6698 ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
6701 if (scraper->Content() == CONTENT_TVSHOWS)
6702 { // check for episodes or seasons. Assumptions are:
6703 // 1. if episodes are in the path then we're in episodes.
6704 // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
6705 // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
6706 CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
6707 m_pDS->query( sql.c_str() );
6708 if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
6710 return foundDirectly ? "tvshows" : "seasons";
6712 return TranslateContent(scraper->Content());
6717 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
6723 if (NULL == m_pDB.get()) return;
6724 if (NULL == m_pDS.get()) return;
6726 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6727 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());
6729 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6730 m_pDS->query( strSQL.c_str() );
6732 while (!m_pDS->eof())
6734 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6735 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6736 g_settings.m_videoSources))
6742 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6744 strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6745 pItem->SetPath("videodb://1/1/"+ strDir);
6746 pItem->m_bIsFolder=true;
6754 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6758 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
6764 if (NULL == m_pDB.get()) return;
6765 if (NULL == m_pDS.get()) return;
6767 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6768 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());
6770 strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
6771 m_pDS->query( strSQL.c_str() );
6773 while (!m_pDS->eof())
6775 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6776 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6777 g_settings.m_videoSources))
6783 CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
6785 strDir.Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
6786 pItem->SetPath("videodb://1/1/"+ strDir);
6787 pItem->m_bIsFolder=true;
6795 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6799 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
6805 if (NULL == m_pDB.get()) return;
6806 if (NULL == m_pDS.get()) return;
6808 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6809 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());
6811 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6812 m_pDS->query( strSQL.c_str() );
6814 while (!m_pDS->eof())
6816 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6817 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6823 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6825 strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6826 pItem->SetPath("videodb://2/1/"+ strDir);
6827 pItem->m_bIsFolder=true;
6835 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6839 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
6845 if (NULL == m_pDB.get()) return;
6846 if (NULL == m_pDS.get()) return;
6848 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6849 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());
6851 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());
6852 m_pDS->query( strSQL.c_str() );
6854 while (!m_pDS->eof())
6856 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6857 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6863 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6865 strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6866 pItem->SetPath("videodb://1/4/"+ strDir);
6867 pItem->m_bIsFolder=true;
6875 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6879 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
6885 if (NULL == m_pDB.get()) return;
6886 if (NULL == m_pDS.get()) return;
6888 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6889 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());
6891 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());
6892 m_pDS->query( strSQL.c_str() );
6894 while (!m_pDS->eof())
6896 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6897 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6903 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6905 strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6906 pItem->SetPath("videodb://2/4/"+ strDir);
6907 pItem->m_bIsFolder=true;
6915 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6919 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
6925 if (NULL == m_pDB.get()) return;
6926 if (NULL == m_pDS.get()) return;
6929 if (!strSearch.IsEmpty())
6930 strLike = "and actors.strActor like '%%%s%%'";
6931 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6932 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());
6934 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
6935 m_pDS->query( strSQL.c_str() );
6937 while (!m_pDS->eof())
6939 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6940 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6946 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6948 strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
6949 pItem->SetPath("videodb://3/4/"+ strDir);
6950 pItem->m_bIsFolder=true;
6958 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6962 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
6968 if (NULL == m_pDB.get()) return;
6969 if (NULL == m_pDS.get()) return;
6971 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6972 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());
6974 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
6975 m_pDS->query( strSQL.c_str() );
6977 while (!m_pDS->eof())
6979 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6980 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6986 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6988 strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6989 pItem->SetPath("videodb://3/1/"+ strDir);
6990 pItem->m_bIsFolder=true;
6998 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7002 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7008 if (NULL == m_pDB.get()) return;
7009 if (NULL == m_pDS.get()) return;
7012 if (!strSearch.IsEmpty())
7014 strLike.Format("and musicvideo.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
7015 strLike += "like '%%s%%%'";
7017 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7018 strSQL=PrepareSQL("select distinct musicvideo.c%02d,musicvideo.idMVideo,path.strPath from musicvideo,files,path where files.idFile=musicvideo.idFile and files.idPath=path.idPath"+strLike,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7021 if (!strLike.IsEmpty())
7022 strLike = "where "+strLike.Mid(4);
7023 strSQL=PrepareSQL("select distinct musicvideo.c%02d,musicvideo.idMVideo from musicvideo"+strLike,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7025 m_pDS->query( strSQL.c_str() );
7027 while (!m_pDS->eof())
7029 if (m_pDS->fv(0).get_asString().empty())
7035 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7036 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7042 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7044 strDir.Format("%ld", m_pDS->fv(1).get_asInt());
7045 pItem->SetPath("videodb://3/2/"+ strDir);
7046 pItem->m_bIsFolder=false;
7054 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7058 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7064 if (NULL == m_pDB.get()) return;
7065 if (NULL == m_pDS.get()) return;
7067 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7068 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());
7070 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());
7071 m_pDS->query( strSQL.c_str() );
7073 while (!m_pDS->eof())
7075 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7076 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7082 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7084 strDir.Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7086 pItem->SetPath("videodb://"+ strDir);
7087 pItem->m_bIsFolder=false;
7095 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7099 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7106 if (NULL == m_pDB.get()) return false;
7107 if (NULL == m_pDS.get()) return false;
7111 CStdString strSQL = "select %s from musicvideoview ";
7112 CVideoDbUrl videoUrl;
7113 CStdString strSQLExtra;
7114 Filter extFilter = filter;
7115 SortDescription sorting = sortDescription;
7116 if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7119 // Apply the limiting directly here if there's no special sorting but limiting
7120 if (extFilter.limit.empty() &&
7121 sorting.sortBy == SortByNone &&
7122 (sorting.limitStart > 0 || sorting.limitEnd > 0))
7124 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7125 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7128 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7130 int iRowsFound = RunQuery(strSQL);
7131 if (iRowsFound <= 0)
7132 return iRowsFound == 0;
7134 // store the total value of items as a property
7135 if (total < iRowsFound)
7137 items.SetProperty("total", total);
7139 DatabaseResults results;
7140 results.reserve(iRowsFound);
7141 if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7144 // get data from returned rows
7145 items.Reserve(results.size());
7146 // get songs from returned subtable
7147 const query_data &data = m_pDS->get_result_set().records;
7148 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7150 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7151 const dbiplus::sql_record* const record = data.at(targetRow);
7153 CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7154 if (!checkLocks || g_settings.GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7155 g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, g_settings.m_videoSources))
7157 CFileItemPtr item(new CFileItem(musicvideo));
7159 CVideoDbUrl itemUrl = videoUrl;
7160 CStdString path; path.Format("%ld", record->at(0).get_asInt());
7161 itemUrl.AppendPath(path);
7162 item->SetPath(itemUrl.ToString());
7164 item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7175 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7180 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7184 if (NULL == m_pDB.get()) return 0;
7185 if (NULL == m_pDS.get()) return 0;
7187 CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7188 if (!m_pDS->query(strSQL.c_str())) return 0;
7190 if (m_pDS->num_rows() == 0)
7195 songIDs.reserve(m_pDS->num_rows());
7196 while (!m_pDS->eof())
7198 songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7202 return songIDs.size();
7206 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7211 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7217 int iCount = GetMusicVideoCount(strWhere);
7220 int iRandom = rand() % iCount;
7222 if (NULL == m_pDB.get()) return false;
7223 if (NULL == m_pDS.get()) return false;
7225 // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7227 strSQL.Format("select * from musicvideoview where %s order by idMVideo limit 1 offset %i",strWhere.c_str(),iRandom);
7228 CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7230 if (!m_pDS->query(strSQL.c_str()))
7232 int iRowsFound = m_pDS->num_rows();
7233 if (iRowsFound != 1)
7238 *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7239 CStdString path; path.Format("videodb://3/2/%ld",item->GetVideoInfoTag()->m_iDbId);
7240 item->SetPath(path);
7241 idSong = m_pDS->fv("idMVideo").get_asInt();
7242 item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7248 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7253 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7257 if (NULL == m_pDB.get()) return -1;
7258 if (NULL == m_pDS.get()) return -1;
7261 if (strAlbum.IsEmpty() && strTitle.IsEmpty())
7262 { // we want to return matching artists only
7263 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7264 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());
7266 strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7269 { // we want to return the matching musicvideo
7270 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7271 strSQL = PrepareSQL("select musicvideo.idMVideo from musicvideo,files,path,artistlinkmusicvideo,actors where files.idFile=musicvideo.idFile and files.idPath=path.idPath and musicvideo.%c02d like '%s' and musicvideo.%c02d like '%s' and artistlinkmusicvideo.idMVideo=musicvideo.idMVideo and artistlinkmusicvideo.idArtist=actors.idActors and actors.strActor like '%s'",VIDEODB_ID_MUSICVIDEO_ALBUM,strAlbum.c_str(),VIDEODB_ID_MUSICVIDEO_TITLE,strTitle.c_str(),strArtist.c_str());
7273 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());
7275 m_pDS->query( strSQL.c_str() );
7280 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7281 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7287 int lResult = m_pDS->fv(0).get_asInt();
7293 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7298 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7304 if (NULL == m_pDB.get()) return;
7305 if (NULL == m_pDS.get()) return;
7307 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7308 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());
7310 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());
7311 m_pDS->query( strSQL.c_str() );
7313 while (!m_pDS->eof())
7315 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7316 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7322 int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7323 int setId = m_pDS->fv("movie.idSet").get_asInt();
7324 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7327 path.Format("videodb://1/2/%i", movieId);
7329 path.Format("videodb://1/7/%i/%i", setId, movieId);
7330 pItem->SetPath(path);
7331 pItem->m_bIsFolder=false;
7339 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7343 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7349 if (NULL == m_pDB.get()) return;
7350 if (NULL == m_pDS.get()) return;
7352 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7353 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());
7355 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());
7356 m_pDS->query( strSQL.c_str() );
7358 while (!m_pDS->eof())
7360 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7361 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7367 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7369 strDir.Format("2/2/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7371 pItem->SetPath("videodb://"+ strDir);
7372 pItem->m_bIsFolder=true;
7373 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7381 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7385 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7391 if (NULL == m_pDB.get()) return;
7392 if (NULL == m_pDS.get()) return;
7394 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7395 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());
7397 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());
7398 m_pDS->query( strSQL.c_str() );
7400 while (!m_pDS->eof())
7402 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7403 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7409 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7410 CStdString path; path.Format("videodb://2/2/%ld/%ld/%ld",m_pDS->fv("episode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
7411 pItem->SetPath(path);
7412 pItem->m_bIsFolder=false;
7420 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7424 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7426 // Alternative searching - not quite as fast though due to
7427 // retrieving all information
7428 // 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()));
7429 // GetMusicVideosByWhere("videodb://3/2/", filter, items);
7434 if (NULL == m_pDB.get()) return;
7435 if (NULL == m_pDS.get()) return;
7437 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7438 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());
7440 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());
7441 m_pDS->query( strSQL.c_str() );
7443 while (!m_pDS->eof())
7445 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7446 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7452 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7454 strDir.Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7456 pItem->SetPath("videodb://"+ strDir);
7457 pItem->m_bIsFolder=false;
7465 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7469 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7471 // Alternative searching - not quite as fast though due to
7472 // retrieving all information
7474 // 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());
7475 // 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());
7476 // GetEpisodesByWhere("videodb://2/2/", filter, items);
7482 if (NULL == m_pDB.get()) return;
7483 if (NULL == m_pDS.get()) return;
7485 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7486 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());
7488 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());
7489 m_pDS->query( strSQL.c_str() );
7491 while (!m_pDS->eof())
7493 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7494 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7500 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7501 CStdString path; path.Format("videodb://2/2/%ld/%ld/%ld",m_pDS->fv("episode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
7502 pItem->SetPath(path);
7503 pItem->m_bIsFolder=false;
7511 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7515 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7521 if (NULL == m_pDB.get()) return;
7522 if (NULL == m_pDS.get()) return;
7524 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7525 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());
7527 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());
7529 m_pDS->query( strSQL.c_str() );
7531 while (!m_pDS->eof())
7533 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7534 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),g_settings.m_videoSources))
7540 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7541 CStdString path; path.Format("videodb://1/2/%ld", m_pDS->fv(0).get_asInt());
7542 pItem->SetPath(path);
7543 pItem->m_bIsFolder=false;
7553 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7557 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7563 if (NULL == m_pDB.get()) return;
7564 if (NULL == m_pDS.get()) return;
7566 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7567 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());
7569 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());
7571 m_pDS->query( strSQL.c_str() );
7573 while (!m_pDS->eof())
7575 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7576 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7583 strDir.Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7584 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7586 pItem->SetPath("videodb://1/5/"+ strDir);
7587 pItem->m_bIsFolder=true;
7595 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7599 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7605 if (NULL == m_pDB.get()) return;
7606 if (NULL == m_pDS.get()) return;
7608 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7609 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());
7611 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());
7613 m_pDS->query( strSQL.c_str() );
7615 while (!m_pDS->eof())
7617 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7618 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7625 strDir.Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7626 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7628 pItem->SetPath("videodb://2/5/"+ strDir);
7629 pItem->m_bIsFolder=true;
7637 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7641 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7647 if (NULL == m_pDB.get()) return;
7648 if (NULL == m_pDS.get()) return;
7650 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7651 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());
7653 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());
7655 m_pDS->query( strSQL.c_str() );
7657 while (!m_pDS->eof())
7659 if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7660 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
7667 strDir.Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7668 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7670 pItem->SetPath("videodb://3/5/"+ strDir);
7671 pItem->m_bIsFolder=true;
7679 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7683 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7685 CGUIDialogProgress *progress=NULL;
7688 if (NULL == m_pDB.get()) return;
7689 if (NULL == m_pDS.get()) return;
7691 unsigned int time = XbmcThreads::SystemClockMillis();
7692 CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
7693 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
7697 // find all the files
7701 if (paths->size() == 0)
7703 RollbackTransaction();
7704 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7708 CStdString strPaths;
7709 for (std::set<int>::const_iterator i = paths->begin(); i != paths->end(); ++i)
7710 strPaths.AppendFormat(",%i",*i);
7711 sql = PrepareSQL("select * from files,path where files.idPath=path.idPath and path.idPath in (%s)",strPaths.Mid(1).c_str());
7714 sql = "select * from files, path where files.idPath = path.idPath";
7716 m_pDS->query(sql.c_str());
7717 if (m_pDS->num_rows() == 0) return;
7721 handle->SetTitle(g_localizeStrings.Get(700));
7722 handle->SetText("");
7724 else if (showProgress)
7726 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
7729 progress->SetHeading(700);
7730 progress->SetLine(0, "");
7731 progress->SetLine(1, 313);
7732 progress->SetLine(2, 330);
7733 progress->SetPercentage(0);
7734 progress->StartModal();
7735 progress->ShowProgressBar(true);
7739 CStdString filesToDelete = "";
7740 CStdString moviesToDelete = "";
7741 CStdString episodesToDelete = "";
7742 CStdString musicVideosToDelete = "";
7744 std::vector<int> movieIDs;
7745 std::vector<int> episodeIDs;
7746 std::vector<int> musicVideoIDs;
7748 int total = m_pDS->num_rows();
7752 VECSOURCES *pShares = g_settings.GetSourcesFromType("video");
7754 while (!m_pDS->eof())
7756 CStdString path = m_pDS->fv("path.strPath").get_asString();
7757 CStdString fileName = m_pDS->fv("files.strFileName").get_asString();
7758 CStdString fullPath;
7759 ConstructPath(fullPath,path,fileName);
7761 // get the first stacked file
7762 if (URIUtils::IsStack(fullPath))
7763 fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
7765 // check if we have a internet related file that is part of a media source
7766 if (URIUtils::IsInternetStream(fullPath, true) && CUtil::GetMatchingSource(fullPath, *pShares, bIsSource) > -1)
7768 if (!CFile::Exists(fullPath, false))
7769 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
7773 // remove optical, internet related and non-existing files
7774 // note: this will also remove entries from previously existing media sources
7775 if (URIUtils::IsOnDVD(fullPath) || URIUtils::IsInternetStream(fullPath, true) || !CFile::Exists(fullPath, false))
7776 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
7783 progress->SetPercentage(current * 100 / total);
7784 progress->Progress();
7785 if (progress->IsCanceled())
7789 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7795 handle->SetPercentage(current/(float)total*100);
7802 // Add any files that don't have a valid idPath entry to the filesToDelete list.
7803 sql = "select files.idFile from files where idPath not in (select idPath from path)";
7804 m_pDS->query(sql.c_str());
7805 while (!m_pDS->eof())
7807 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
7812 if ( ! filesToDelete.IsEmpty() )
7814 filesToDelete.TrimRight(",");
7815 // now grab them movies
7816 sql = PrepareSQL("select idMovie from movie where idFile in (%s)",filesToDelete.c_str());
7817 m_pDS->query(sql.c_str());
7818 while (!m_pDS->eof())
7820 movieIDs.push_back(m_pDS->fv(0).get_asInt());
7821 moviesToDelete += m_pDS->fv(0).get_asString() + ",";
7825 // now grab them episodes
7826 sql = PrepareSQL("select idEpisode from episode where idFile in (%s)",filesToDelete.c_str());
7827 m_pDS->query(sql.c_str());
7828 while (!m_pDS->eof())
7830 episodeIDs.push_back(m_pDS->fv(0).get_asInt());
7831 episodesToDelete += m_pDS->fv(0).get_asString() + ",";
7836 // now grab them musicvideos
7837 sql = PrepareSQL("select idMVideo from musicvideo where idFile in (%s)",filesToDelete.c_str());
7838 m_pDS->query(sql.c_str());
7839 while (!m_pDS->eof())
7841 musicVideoIDs.push_back(m_pDS->fv(0).get_asInt());
7842 musicVideosToDelete += m_pDS->fv(0).get_asString() + ",";
7850 progress->SetPercentage(100);
7851 progress->Progress();
7854 if ( ! filesToDelete.IsEmpty() )
7856 filesToDelete = "(" + filesToDelete + ")";
7857 CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
7858 sql = "delete from files where idFile in " + filesToDelete;
7859 m_pDS->exec(sql.c_str());
7861 CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
7862 sql = "delete from streamdetails where idFile in " + filesToDelete;
7863 m_pDS->exec(sql.c_str());
7865 CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
7866 sql = "delete from bookmark where idFile in " + filesToDelete;
7867 m_pDS->exec(sql.c_str());
7869 CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
7870 sql = "delete from settings where idFile in " + filesToDelete;
7871 m_pDS->exec(sql.c_str());
7873 CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
7874 sql = "delete from stacktimes where idFile in " + filesToDelete;
7875 m_pDS->exec(sql.c_str());
7878 if ( ! moviesToDelete.IsEmpty() )
7880 moviesToDelete = "(" + moviesToDelete.TrimRight(",") + ")";
7882 CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
7883 sql = "delete from movie where idMovie in " + moviesToDelete;
7884 m_pDS->exec(sql.c_str());
7886 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
7887 sql = "delete from actorlinkmovie where idMovie in " + moviesToDelete;
7888 m_pDS->exec(sql.c_str());
7890 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
7891 sql = "delete from directorlinkmovie where idMovie in " + moviesToDelete;
7892 m_pDS->exec(sql.c_str());
7894 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
7895 sql = "delete from writerlinkmovie where idMovie in " + moviesToDelete;
7896 m_pDS->exec(sql.c_str());
7898 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
7899 sql = "delete from genrelinkmovie where idMovie in " + moviesToDelete;
7900 m_pDS->exec(sql.c_str());
7902 CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
7903 sql = "delete from countrylinkmovie where idMovie in " + moviesToDelete;
7904 m_pDS->exec(sql.c_str());
7906 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
7907 sql = "delete from studiolinkmovie where idMovie in " + moviesToDelete;
7908 m_pDS->exec(sql.c_str());
7911 if ( ! episodesToDelete.IsEmpty() )
7913 episodesToDelete = "(" + episodesToDelete.TrimRight(",") + ")";
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 * from path where not (strContent='' and strSettings='' and strHash='' and exclude!=1)";
7934 m_pDS->query(sql.c_str());
7936 while (!m_pDS->eof())
7938 if (!CDirectory::Exists(m_pDS->fv("path.strPath").get_asString()))
7939 strIds.AppendFormat("%i,", m_pDS->fv("path.idPath").get_asInt());
7943 if (!strIds.IsEmpty())
7945 strIds.TrimRight(",");
7946 sql = PrepareSQL("delete from path where idPath in (%s)",strIds.c_str());
7947 m_pDS->exec(sql.c_str());
7948 sql = PrepareSQL("delete from tvshowlinkpath where idPath in (%s)",strIds.c_str());
7949 m_pDS->exec(sql.c_str());
7951 sql = "delete from tvshowlinkpath where idPath not in (select idPath from path)";
7952 m_pDS->exec(sql.c_str());
7954 CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
7955 sql = "delete from tvshow where idShow not in (select idShow from tvshowlinkpath)";
7956 m_pDS->exec(sql.c_str());
7958 std::vector<int> tvshowIDs;
7959 CStdString showsToDelete;
7960 sql = "select tvshow.idShow from tvshow "
7961 "join tvshowlinkpath on tvshow.idShow=tvshowlinkpath.idShow "
7962 "join path on path.idPath=tvshowlinkpath.idPath "
7963 "where tvshow.idShow not in (select idShow from episode) "
7964 "and path.strContent=''";
7965 m_pDS->query(sql.c_str());
7966 while (!m_pDS->eof())
7968 tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
7969 showsToDelete += m_pDS->fv(0).get_asString() + ",";
7973 if (!showsToDelete.IsEmpty())
7975 sql = "delete from tvshow where idShow in (" + showsToDelete.TrimRight(",") + ")";
7976 m_pDS->exec(sql.c_str());
7979 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
7980 sql = "delete from actorlinktvshow where idShow not in (select idShow from tvshow)";
7981 m_pDS->exec(sql.c_str());
7983 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
7984 sql = "delete from directorlinktvshow where idShow not in (select idShow from tvshow)";
7985 m_pDS->exec(sql.c_str());
7987 CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
7988 sql = "delete from tvshowlinkpath where idShow not in (select idShow from tvshow)";
7989 m_pDS->exec(sql.c_str());
7991 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
7992 sql = "delete from genrelinktvshow where idShow not in (select idShow from tvshow)";
7993 m_pDS->exec(sql.c_str());
7995 CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
7996 sql = "delete from seasons where idShow not in (select idShow from tvshow)";
7997 m_pDS->exec(sql.c_str());
7999 CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
8000 sql = "delete from movielinktvshow where idShow not in (select idShow from tvshow)";
8001 m_pDS->exec(sql.c_str());
8002 sql = "delete from movielinktvshow where idMovie not in (select distinct idMovie from movie)";
8003 m_pDS->exec(sql.c_str());
8005 if ( ! musicVideosToDelete.IsEmpty() )
8007 musicVideosToDelete = "(" + musicVideosToDelete.TrimRight(",") + ")";
8009 CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8010 sql = "delete from musicvideo where idMVideo in " + musicVideosToDelete;
8011 m_pDS->exec(sql.c_str());
8013 CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8014 sql = "delete from artistlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8015 m_pDS->exec(sql.c_str());
8017 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8018 sql = "delete from directorlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8019 m_pDS->exec(sql.c_str());
8021 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8022 sql = "delete from genrelinkmusicvideo where idMVideo in " + musicVideosToDelete;
8023 m_pDS->exec(sql.c_str());
8025 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8026 sql = "delete from studiolinkmusicvideo where idMVideo in " + musicVideosToDelete;
8027 m_pDS->exec(sql.c_str());
8030 CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8031 sql.Format("delete from path where strContent='' and strSettings='' and strHash='' and exclude!=1 "
8032 "and idPath not in (select distinct idPath from files) "
8033 "and idPath not in (select distinct idPath from tvshowlinkpath) "
8034 "and idPath not in (select distinct c%02d from movie) "
8035 "and idPath not in (select distinct c%02d from tvshow) "
8036 "and idPath not in (select distinct c%02d from episode) "
8037 "and idPath not in (select distinct c%02d from musicvideo)"
8038 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8039 m_pDS->exec(sql.c_str());
8041 CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8042 sql = "delete from genre where idGenre not in (select distinct idGenre from genrelinkmovie) and idGenre not in (select distinct idGenre from genrelinktvshow) and idGenre not in (select distinct idGenre from genrelinkmusicvideo)";
8043 m_pDS->exec(sql.c_str());
8045 CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8046 sql = "delete from country where idCountry not in (select distinct idCountry from countrylinkmovie)";
8047 m_pDS->exec(sql.c_str());
8049 CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8050 sql = "delete from actors where idActor not in (select distinct idActor from actorlinkmovie) and idActor not in (select distinct idDirector from directorlinkmovie) and idActor not in (select distinct idWriter from writerlinkmovie) and idActor not in (select distinct idActor from actorlinktvshow) and idActor not in (select distinct idActor from actorlinkepisode) and idActor not in (select distinct idDirector from directorlinktvshow) and idActor not in (select distinct idDirector from directorlinkepisode) and idActor not in (select distinct idWriter from writerlinkepisode) and idActor not in (select distinct idArtist from artistlinkmusicvideo) and idActor not in (select distinct idDirector from directorlinkmusicvideo)";
8051 m_pDS->exec(sql.c_str());
8053 CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8054 sql = "delete from studio where idStudio not in (select distinct idStudio from studiolinkmovie) and idStudio not in (select distinct idStudio from studiolinkmusicvideo) and idStudio not in (select distinct idStudio from studiolinktvshow)";
8055 m_pDS->exec(sql.c_str());
8057 CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8058 sql = "delete from sets where idSet not in (select distinct idSet from movie)";
8059 m_pDS->exec(sql.c_str());
8061 CommitTransaction();
8064 handle->SetTitle(g_localizeStrings.Get(331));
8068 CUtil::DeleteVideoDatabaseDirectoryCache();
8070 time = XbmcThreads::SystemClockMillis() - time;
8071 CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8073 for (unsigned int i = 0; i < movieIDs.size(); i++)
8074 AnnounceRemove("movie", movieIDs[i]);
8076 for (unsigned int i = 0; i < episodeIDs.size(); i++)
8077 AnnounceRemove("episode", episodeIDs[i]);
8079 for (unsigned int i = 0; i < tvshowIDs.size(); i++)
8080 AnnounceRemove("tvshow", tvshowIDs[i]);
8082 for (unsigned int i = 0; i < musicVideoIDs.size(); i++)
8083 AnnounceRemove("musicvideo", musicVideoIDs[i]);
8087 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8092 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8095 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8098 CFileItemList items;
8099 GetTvShowsByWhere("videodb://2/2/", "", items);
8100 CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8101 CDirectory::Create(showPath);
8102 for (int i = 0; i < items.Size(); i++)
8104 // create a folder in this directory
8105 CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8106 CStdString TVFolder;
8107 URIUtils::AddFileToFolder(showPath, showName, TVFolder);
8108 if (CDirectory::Create(TVFolder))
8109 { // right - grab the episodes and dump them as well
8110 CFileItemList episodes;
8111 Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8112 GetEpisodesByWhere("videodb://2/2/", filter, episodes);
8113 for (int i = 0; i < episodes.Size(); i++)
8115 CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8117 episode.Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8119 CStdString episodePath;
8120 URIUtils::AddFileToFolder(TVFolder, episode, episodePath);
8122 if (file.OpenForWrite(episodePath))
8129 GetMoviesByWhere("videodb://1/2/", "", items);
8130 CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8131 CDirectory::Create(moviePath);
8132 for (int i = 0; i < items.Size(); i++)
8134 CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8136 movie.Format("%s.avi", tag->m_strTitle.c_str());
8138 if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8143 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8145 CGUIDialogProgress *progress=NULL;
8148 if (NULL == m_pDB.get()) return;
8149 if (NULL == m_pDS.get()) return;
8150 if (NULL == m_pDS2.get()) return;
8152 // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8153 auto_ptr<Dataset> pDS;
8154 pDS.reset(m_pDB->CreateDataset());
8155 if (NULL == pDS.get()) return;
8157 auto_ptr<Dataset> pDS2;
8158 pDS2.reset(m_pDB->CreateDataset());
8159 if (NULL == pDS2.get()) return;
8161 // if we're exporting to a single folder, we export thumbs as well
8162 CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8163 CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8164 CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8165 CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8166 CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8167 CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8173 CDirectory::Remove(exportRoot);
8174 CDirectory::Create(exportRoot);
8175 CDirectory::Create(actorsDir);
8176 CDirectory::Create(moviesDir);
8177 CDirectory::Create(musicvideosDir);
8178 CDirectory::Create(tvshowsDir);
8181 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8183 CStdString sql = "select * from movieview";
8185 m_pDS->query(sql.c_str());
8189 progress->SetHeading(647);
8190 progress->SetLine(0, 650);
8191 progress->SetLine(1, "");
8192 progress->SetLine(2, "");
8193 progress->SetPercentage(0);
8194 progress->StartModal();
8195 progress->ShowProgressBar(true);
8198 int total = m_pDS->num_rows();
8201 // create our xml document
8202 CXBMCTinyXML xmlDoc;
8203 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8204 xmlDoc.InsertEndChild(decl);
8205 TiXmlNode *pMain = NULL;
8210 TiXmlElement xmlMainElement("videodb");
8211 pMain = xmlDoc.InsertEndChild(xmlMainElement);
8212 XMLUtils::SetInt(pMain,"version", GetExportVersion());
8215 while (!m_pDS->eof())
8217 CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8218 // strip paths to make them relative
8219 if (movie.m_strTrailer.Mid(0,movie.m_strPath.size()).Equals(movie.m_strPath))
8220 movie.m_strTrailer = movie.m_strTrailer.Mid(movie.m_strPath.size());
8221 map<string, string> artwork;
8222 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8224 TiXmlElement additionalNode("art");
8225 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8226 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8227 movie.Save(pMain, "movie", true, &additionalNode);
8230 movie.Save(pMain, "movie", !singleFiles);
8232 // reset old skip state
8237 progress->SetLine(1, movie.m_strTitle);
8238 progress->SetPercentage(current * 100 / total);
8239 progress->Progress();
8240 if (progress->IsCanceled())
8248 CFileItem item(movie.m_strFileNameAndPath,false);
8249 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8251 if (!item.Exists(false))
8253 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8258 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8260 if (item.IsOpticalMediaFile())
8262 nfoFile = URIUtils::GetParentFolderURI(nfoFile, true);
8265 if (overwrite || !CFile::Exists(nfoFile, false))
8267 if(!xmlDoc.SaveFile(nfoFile))
8269 CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8270 bSkip = ExportSkipEntry(nfoFile);
8285 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8286 xmlDoc.InsertEndChild(decl);
8289 if (images && !bSkip)
8293 CStdString strFileName(movie.m_strTitle);
8294 if (movie.m_iYear > 0)
8295 strFileName.AppendFormat("_%i", movie.m_iYear);
8296 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8298 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8300 CStdString savedThumb = item.GetLocalArt(i->first, false);
8301 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8304 ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8311 // find all musicvideos
8312 sql = "select * from musicvideoview";
8314 m_pDS->query(sql.c_str());
8316 total = m_pDS->num_rows();
8319 while (!m_pDS->eof())
8321 CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8322 map<string, string> artwork;
8323 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8325 TiXmlElement additionalNode("art");
8326 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8327 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8328 movie.Save(pMain, "musicvideo", true, &additionalNode);
8331 movie.Save(pMain, "musicvideo", !singleFiles);
8333 // reset old skip state
8338 progress->SetLine(1, movie.m_strTitle);
8339 progress->SetPercentage(current * 100 / total);
8340 progress->Progress();
8341 if (progress->IsCanceled())
8349 CFileItem item(movie.m_strFileNameAndPath,false);
8350 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8352 if (!item.Exists(false))
8354 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8359 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8361 if (overwrite || !CFile::Exists(nfoFile, false))
8363 if(!xmlDoc.SaveFile(nfoFile))
8365 CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8366 bSkip = ExportSkipEntry(nfoFile);
8381 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8382 xmlDoc.InsertEndChild(decl);
8384 if (images && !bSkip)
8388 CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8389 if (movie.m_iYear > 0)
8390 strFileName.AppendFormat("_%i", movie.m_iYear);
8391 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8393 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8395 CStdString savedThumb = item.GetLocalArt(i->first, false);
8396 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8404 // repeat for all tvshows
8405 sql = "SELECT * FROM tvshowview";
8406 m_pDS->query(sql.c_str());
8408 total = m_pDS->num_rows();
8411 while (!m_pDS->eof())
8413 CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8415 map<int, map<string, string> > seasonArt;
8416 GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8418 map<string, string> artwork;
8419 if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8421 TiXmlElement additionalNode("art");
8422 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8423 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8424 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8426 TiXmlElement seasonNode("season");
8427 seasonNode.SetAttribute("num", i->first);
8428 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8429 XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8430 additionalNode.InsertEndChild(seasonNode);
8432 tvshow.Save(pMain, "tvshow", true, &additionalNode);
8435 tvshow.Save(pMain, "tvshow", !singleFiles);
8437 // reset old skip state
8442 progress->SetLine(1, tvshow.m_strTitle);
8443 progress->SetPercentage(current * 100 / total);
8444 progress->Progress();
8445 if (progress->IsCanceled())
8453 // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8454 if (URIUtils::IsMultiPath(tvshow.m_strPath))
8455 tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8457 CFileItem item(tvshow.m_strPath, true);
8458 if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8460 if (!item.Exists(false))
8462 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8468 URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo", nfoFile);
8470 if (overwrite || !CFile::Exists(nfoFile, false))
8472 if(!xmlDoc.SaveFile(nfoFile))
8474 CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8475 bSkip = ExportSkipEntry(nfoFile);
8490 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8491 xmlDoc.InsertEndChild(decl);
8493 if (images && !bSkip)
8496 item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8498 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8500 CStdString savedThumb = item.GetLocalArt(i->first, true);
8501 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8505 ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8507 // export season thumbs
8508 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8512 seasonThumb = "season-all";
8513 else if (i->first == 0)
8514 seasonThumb = "season-specials";
8516 seasonThumb = StringUtils::Format("season%02i", i->first);
8517 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8519 CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8520 if (!i->second.empty())
8521 CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8526 // now save the episodes from this show
8527 sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8528 pDS->query(sql.c_str());
8529 CStdString showDir(item.GetPath());
8533 CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8534 map<string, string> artwork;
8535 if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8537 TiXmlElement additionalNode("art");
8538 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8539 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8540 episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8542 else if (!singleFiles)
8543 episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8545 episode.Save(pMain, "episodedetails", !singleFiles);
8547 // multi-episode files need dumping to the same XML
8548 while (singleFiles && !pDS->eof() &&
8549 episode.m_iFileId == pDS->fv("idFile").get_asInt())
8551 episode = GetDetailsForEpisode(pDS, true);
8552 episode.Save(pMain, "episodedetails", !singleFiles);
8556 // reset old skip state
8559 CFileItem item(episode.m_strFileNameAndPath, false);
8560 if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8562 if (!item.Exists(false))
8564 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8569 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8571 if (overwrite || !CFile::Exists(nfoFile, false))
8573 if(!xmlDoc.SaveFile(nfoFile))
8575 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8576 bSkip = ExportSkipEntry(nfoFile);
8591 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8592 xmlDoc.InsertEndChild(decl);
8595 if (images && !bSkip)
8600 epName.Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8601 item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8603 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8605 CStdString savedThumb = item.GetLocalArt(i->first, false);
8606 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8609 ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8618 if (singleFiles && progress)
8620 progress->SetPercentage(100);
8621 progress->Progress();
8626 // now dump path info
8627 set<CStdString> paths;
8629 TiXmlElement xmlPathElement("paths");
8630 TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8631 for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8633 bool foundDirectly = false;
8634 SScanSettings settings;
8635 ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8636 if (info && foundDirectly)
8638 TiXmlElement xmlPathElement2("path");
8639 TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8640 XMLUtils::SetString(pPath,"url", *iter);
8641 XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8642 XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8643 XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8644 XMLUtils::SetString(pPath,"scraperpath", info->ID());
8647 xmlDoc.SaveFile(xmlFile);
8652 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8659 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8661 CStdString strPath(strDir);
8664 strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8665 if (!CDirectory::Exists(strPath))
8667 CDirectory::Create(strPath);
8668 CFile::SetHidden(strPath, true);
8672 for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8675 item.SetLabel(iter->strName);
8676 if (!iter->thumb.IsEmpty())
8678 CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8679 CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8684 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8686 CStdString strParent;
8687 URIUtils::GetParentPath(nfoFile,strParent);
8688 CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8690 bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8693 CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8695 CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8700 void CVideoDatabase::ImportFromXML(const CStdString &path)
8702 CGUIDialogProgress *progress=NULL;
8705 if (NULL == m_pDB.get()) return;
8706 if (NULL == m_pDS.get()) return;
8708 CXBMCTinyXML xmlDoc;
8709 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
8712 TiXmlElement *root = xmlDoc.RootElement();
8715 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8718 progress->SetHeading(648);
8719 progress->SetLine(0, 649);
8720 progress->SetLine(1, 330);
8721 progress->SetLine(2, "");
8722 progress->SetPercentage(0);
8723 progress->StartModal();
8724 progress->ShowProgressBar(true);
8728 XMLUtils::GetInt(root, "version", iVersion);
8730 CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
8732 TiXmlElement *movie = root->FirstChildElement();
8735 // first count the number of items...
8738 if (strnicmp(movie->Value(), "movie", 5)==0 ||
8739 strnicmp(movie->Value(), "tvshow", 6)==0 ||
8740 strnicmp(movie->Value(), "musicvideo",10)==0 )
8742 movie = movie->NextSiblingElement();
8745 CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
8746 CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
8747 CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
8748 CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
8749 CVideoInfoScanner scanner;
8750 // add paths first (so we have scraper settings available)
8751 TiXmlElement *path = root->FirstChildElement("paths");
8752 path = path->FirstChildElement();
8756 if (XMLUtils::GetString(path,"url",strPath))
8760 if (XMLUtils::GetString(path,"content", content))
8761 { // check the scraper exists, if so store the path
8764 XMLUtils::GetString(path,"scraperpath",id);
8765 if (CAddonMgr::Get().GetAddon(id, addon))
8767 SScanSettings settings;
8768 ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
8769 // FIXME: scraper settings are not exported?
8770 scraper->SetPathSettings(TranslateContent(content), "");
8771 XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
8772 XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
8773 SetScraperForPath(strPath,scraper,settings);
8776 path = path->NextSiblingElement();
8778 movie = root->FirstChildElement();
8782 if (strnicmp(movie->Value(), "movie", 5) == 0)
8785 CFileItem item(info);
8786 bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.GetPath()) : false;
8787 CStdString filename = info.m_strTitle;
8788 if (info.m_iYear > 0)
8789 filename.AppendFormat("_%i", info.m_iYear);
8790 CFileItem artItem(item);
8791 artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
8792 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8793 item.SetArt(artItem.GetArt());
8794 scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
8797 else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
8800 CFileItem item(info);
8801 bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.GetPath()) : false;
8802 CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
8803 if (info.m_iYear > 0)
8804 filename.AppendFormat("_%i", info.m_iYear);
8805 CFileItem artItem(item);
8806 artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
8807 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8808 item.SetArt(artItem.GetArt());
8809 scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
8812 else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
8814 // load the TV show in. NOTE: This deletes all episodes under the TV Show, which may not be
8815 // what we desire. It may make better sense to only delete (or even better, update) the show information
8817 URIUtils::AddSlashAtEnd(info.m_strPath);
8818 DeleteTvShow(info.m_strPath);
8819 CFileItem showItem(info);
8820 bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(showItem.GetPath(), true) : false;
8821 CFileItem artItem(showItem);
8822 CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
8823 artItem.SetPath(artPath);
8824 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8825 showItem.SetArt(artItem.GetArt());
8826 int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
8828 map<int, map<string, string> > seasonArt;
8829 artItem.GetVideoInfoTag()->m_strPath = artPath;
8830 scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
8831 for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8833 int seasonID = AddSeason(showID, i->first);
8834 SetArtForItem(seasonID, "season", i->second);
8837 // now load the episodes
8838 TiXmlElement *episode = movie->FirstChildElement("episodedetails");
8841 // no need to delete the episode info, due to the above deletion
8844 CFileItem item(info);
8845 CStdString filename;
8846 filename.Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
8847 CFileItem artItem(item);
8848 artItem.SetPath(GetSafeFile(artPath, filename));
8849 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
8850 item.SetArt(artItem.GetArt());
8851 scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
8852 episode = episode->NextSiblingElement("episodedetails");
8855 movie = movie->NextSiblingElement();
8856 if (progress && total)
8858 progress->SetPercentage(current * 100 / total);
8859 progress->SetLine(2, info.m_strTitle);
8860 progress->Progress();
8861 if (progress->IsCanceled())
8864 RollbackTransaction();
8872 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8878 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
8880 if (!node) return false;
8881 const TiXmlNode *art = node->FirstChild();
8882 while (art && art->FirstChild())
8884 artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
8885 art = art->NextSibling();
8887 return !artwork.empty();
8890 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
8892 if (URIUtils::IsStack(strFileName) ||
8893 URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
8894 strDest = strFileName;
8896 URIUtils::AddFileToFolder(strPath, strFileName, strDest);
8899 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
8901 if (URIUtils::IsStack(strFileNameAndPath) || strFileNameAndPath.Mid(0,6).Equals("rar://") || strFileNameAndPath.Mid(0,6).Equals("zip://"))
8903 URIUtils::GetParentPath(strFileNameAndPath,strPath);
8904 strFileName = strFileNameAndPath;
8906 else if (URIUtils::IsPlugin(strFileNameAndPath))
8908 CURL url(strFileNameAndPath);
8909 strPath = url.GetWithoutFilename();
8910 strFileName = strFileNameAndPath;
8913 URIUtils::Split(strFileNameAndPath,strPath, strFileName);
8916 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
8918 SScanSettings settings;
8920 ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
8921 SetPathHash(strPath,"");
8924 if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
8926 if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
8928 CStdString strParent;
8929 URIUtils::GetParentPath(strPath,strParent);
8930 SetPathHash(strParent,"");
8935 bool CVideoDatabase::CommitTransaction()
8937 if (CDatabase::CommitTransaction())
8938 { // number of items in the db has likely changed, so recalculate
8939 g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
8940 g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
8941 g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
8947 void CVideoDatabase::SetDetail(const CStdString& strDetail, int id, int field,
8948 VIDEODB_CONTENT_TYPE type)
8952 if (NULL == m_pDB.get()) return;
8953 if (NULL == m_pDS.get()) return;
8955 CStdString strTable, strField;
8956 if (type == VIDEODB_CONTENT_MOVIES)
8959 strField = "idMovie";
8961 if (type == VIDEODB_CONTENT_TVSHOWS)
8963 strTable = "tvshow";
8964 strField = "idShow";
8966 if (type == VIDEODB_CONTENT_MUSICVIDEOS)
8968 strTable = "musicvideo";
8969 strField = "idMVideo";
8972 if (strTable.IsEmpty())
8975 CStdString strSQL = PrepareSQL("update %s set c%02u='%s' where %s=%u",
8976 strTable.c_str(), field, strDetail.c_str(), strField.c_str(), id);
8977 m_pDS->exec(strSQL.c_str());
8981 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8985 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
8987 CStdString safeThumb(name);
8988 safeThumb.Replace(' ', '_');
8989 return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
8992 void CVideoDatabase::AnnounceRemove(std::string content, int id)
8995 data["type"] = content;
8997 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9000 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9003 data["type"] = content;
9005 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9008 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9010 CStdString path(strPath);
9012 if(URIUtils::IsMultiPath(path))
9014 vector<CStdString> paths;
9015 CMultiPathDirectory::GetPaths(path, paths);
9017 for(unsigned i=0;i<paths.size();i++)
9018 GetItemsForPath(content, paths[i], items);
9020 return items.Size() > 0;
9023 int pathID = GetPathId(path);
9027 if (content == "movies")
9029 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9030 GetMoviesByWhere("videodb://1/2/", filter, items);
9032 else if (content == "episodes")
9034 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9035 GetEpisodesByWhere("videodb://2/2/", filter, items);
9037 else if (content == "tvshows")
9039 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9040 GetTvShowsByWhere("videodb://2/2/", filter, items);
9042 else if (content == "musicvideos")
9044 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9045 GetMusicVideosByWhere("videodb://3/2/", filter, items);
9047 for (int i = 0; i < items.Size(); i++)
9048 items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9049 return items.Size() > 0;
9052 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9054 if (!videoUrl.IsValid())
9057 std::string type = videoUrl.GetType();
9058 std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9059 const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9060 CUrlOptions::UrlOptions::const_iterator option;
9062 if (type == "movies")
9064 option = options.find("genreid");
9065 if (option != options.end())
9067 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9068 filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9071 option = options.find("genre");
9072 if (option != options.end())
9074 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9075 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9078 option = options.find("countryid");
9079 if (option != options.end())
9081 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9082 filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9085 option = options.find("country");
9086 if (option != options.end())
9088 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9089 filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9092 option = options.find("studioid");
9093 if (option != options.end())
9095 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9096 filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9099 option = options.find("studio");
9100 if (option != options.end())
9102 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9103 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9106 option = options.find("directorid");
9107 if (option != options.end())
9109 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9110 filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9113 option = options.find("director");
9114 if (option != options.end())
9116 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9117 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9120 option = options.find("year");
9121 if (option != options.end())
9122 filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9124 option = options.find("actorid");
9125 if (option != options.end())
9127 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9128 filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9131 option = options.find("actor");
9132 if (option != options.end())
9134 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9135 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9138 option = options.find("setid");
9139 if (option != options.end())
9140 filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9142 option = options.find("set");
9143 if (option != options.end())
9145 filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9146 filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9149 option = options.find("tagid");
9150 if (option != options.end())
9152 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9153 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9156 option = options.find("tag");
9157 if (option != options.end())
9159 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9160 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9163 else if (type == "tvshows")
9165 if (itemType == "tvshows")
9167 option = options.find("genreid");
9168 if (option != options.end())
9170 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9171 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9174 option = options.find("genre");
9175 if (option != options.end())
9177 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9178 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9181 option = options.find("studioid");
9182 if (option != options.end())
9184 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9185 filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9188 option = options.find("studio");
9189 if (option != options.end())
9191 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9192 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9195 option = options.find("directorid");
9196 if (option != options.end())
9198 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9199 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9202 option = options.find("year");
9203 if (option != options.end())
9204 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9206 option = options.find("actorid");
9207 if (option != options.end())
9209 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9210 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9213 option = options.find("actor");
9214 if (option != options.end())
9216 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9217 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9220 option = options.find("tagid");
9221 if (option != options.end())
9223 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9224 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9227 option = options.find("tag");
9228 if (option != options.end())
9230 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9231 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9234 else if (itemType == "seasons")
9236 option = options.find("genreid");
9237 if (option != options.end())
9239 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9240 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9243 option = options.find("directorid");
9244 if (option != options.end())
9246 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9247 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9250 option = options.find("year");
9251 if (option != options.end())
9252 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9254 option = options.find("actorid");
9255 if (option != options.end())
9257 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9258 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9261 else if (itemType == "episodes")
9264 option = options.find("tvshowid");
9265 if (option != options.end())
9266 idShow = (int)option->second.asInteger();
9269 option = options.find("season");
9270 if (option != options.end())
9271 season = (int)option->second.asInteger();
9273 CStdString strIn = PrepareSQL("= %i", idShow);
9274 GetStackedTvShowList(idShow, strIn);
9278 bool condition = false;
9280 option = options.find("genreid");
9281 if (option != options.end())
9284 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9285 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9288 option = options.find("genre");
9289 if (option != options.end())
9292 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9293 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9296 option = options.find("directorid");
9297 if (option != options.end())
9300 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9301 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9304 option = options.find("director");
9305 if (option != options.end())
9308 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9309 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9312 option = options.find("year");
9313 if (option != options.end())
9316 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9319 option = options.find("actorid");
9320 if (option != options.end())
9323 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9324 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9327 option = options.find("actor");
9328 if (option != options.end())
9331 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9332 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9336 filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9340 if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9341 filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9343 filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9344 VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9349 option = options.find("year");
9350 if (option != options.end())
9351 filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9353 option = options.find("directorid");
9354 if (option != options.end())
9356 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9357 filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9360 option = options.find("director");
9361 if (option != options.end())
9363 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9364 filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9369 else if (type == "musicvideos")
9371 option = options.find("genreid");
9372 if (option != options.end())
9374 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9375 filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9378 option = options.find("genre");
9379 if (option != options.end())
9381 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9382 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9385 option = options.find("studioid");
9386 if (option != options.end())
9388 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9389 filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9392 option = options.find("studio");
9393 if (option != options.end())
9395 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9396 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9399 option = options.find("directorid");
9400 if (option != options.end())
9402 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9403 filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9406 option = options.find("director");
9407 if (option != options.end())
9409 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9410 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9413 option = options.find("year");
9414 if (option != options.end())
9415 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9417 option = options.find("artistid");
9418 if (option != options.end())
9420 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9421 filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9424 option = options.find("artist");
9425 if (option != options.end())
9427 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9428 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9431 option = options.find("albumid");
9432 if (option != options.end())
9433 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()));
9435 option = options.find("tagid");
9436 if (option != options.end())
9438 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9439 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9442 option = options.find("tag");
9443 if (option != options.end())
9445 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9446 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9452 option = options.find("xsp");
9453 if (option != options.end())
9456 if (!xsp.LoadFromJson(option->second.asString()))
9459 // check if the filter playlist matches the item type
9460 if (xsp.GetType() == itemType ||
9461 // handle episode listings with videodb://2/2/ which get the rest
9462 // of the path (season and episodeid) appended later
9463 (xsp.GetType() == "episodes" && itemType == "tvshows"))
9465 std::set<CStdString> playlists;
9466 filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9468 if (xsp.GetLimit() > 0)
9469 sorting.limitEnd = xsp.GetLimit();
9470 if (xsp.GetOrder() != SortByNone)
9471 sorting.sortBy = xsp.GetOrder();
9472 if (xsp.GetOrderDirection() != SortOrderNone)
9473 sorting.sortOrder = xsp.GetOrderDirection();
9474 if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
9475 sorting.sortAttributes = SortAttributeIgnoreArticle;
9479 option = options.find("filter");
9480 if (option != options.end())
9482 CSmartPlaylist xspFilter;
9483 if (!xspFilter.LoadFromJson(option->second.asString()))
9486 // check if the filter playlist matches the item type
9487 if (xspFilter.GetType() == itemType)
9489 std::set<CStdString> playlists;
9490 filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9492 // remove the filter if it doesn't match the item type
9494 videoUrl.RemoveOption("filter");