[cstdstring] remove IsEmpty() and replace with empty()
[vuplus_xbmc] / xbmc / video / VideoDatabase.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
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"
27 #include "Util.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"
41 #include "FileItem.h"
42 #include "profiles/ProfilesManager.h"
43 #include "settings/AdvancedSettings.h"
44 #include "settings/MediaSettings.h"
45 #include "settings/MediaSourceSettings.h"
46 #include "settings/Settings.h"
47 #include "utils/StringUtils.h"
48 #include "guilib/LocalizeStrings.h"
49 #include "utils/TimeUtils.h"
50 #include "utils/log.h"
51 #include "TextureCache.h"
52 #include "addons/AddonInstaller.h"
53 #include "interfaces/AnnouncementManager.h"
54 #include "dbwrappers/dataset.h"
55 #include "utils/LabelFormatter.h"
56 #include "XBDateTime.h"
57 #include "URL.h"
58 #include "video/VideoDbUrl.h"
59 #include "playlists/SmartPlayList.h"
60 #include "utils/GroupUtils.h"
61
62 using namespace std;
63 using namespace dbiplus;
64 using namespace XFILE;
65 using namespace VIDEO;
66 using namespace ADDON;
67
68 //********************************************************************************************************************************
69 CVideoDatabase::CVideoDatabase(void)
70 {
71 }
72
73 //********************************************************************************************************************************
74 CVideoDatabase::~CVideoDatabase(void)
75 {}
76
77 //********************************************************************************************************************************
78 bool CVideoDatabase::Open()
79 {
80   return CDatabase::Open(g_advancedSettings.m_databaseVideo);
81 }
82
83 bool CVideoDatabase::CreateTables()
84 {
85   /* indexes should be added on any columns that are used in in  */
86   /* a where or a join. primary key on a column is the same as a */
87   /* unique index on that column, so there is no need to add any */
88   /* index if no other columns are refered                       */
89
90   /* order of indexes are important, for an index to be considered all  */
91   /* columns up to the column in question have to have been specified   */
92   /* select * from actorlinkmovie where idMovie = 1, can not take       */
93   /* advantage of a index that has been created on ( idGenre, idMovie ) */
94   /*, hower on on ( idMovie, idGenre ) will be considered for use       */
95
96   BeginTransaction();
97   try
98   {
99     CDatabase::CreateTables();
100
101     CLog::Log(LOGINFO, "create bookmark table");
102     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");
103     m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
104
105     CLog::Log(LOGINFO, "create settings table");
106     m_pDS->exec("CREATE TABLE settings ( idFile integer, Deinterlace bool,"
107                 "ViewMode integer,ZoomAmount float, PixelRatio float, VerticalShift float, AudioStream integer, SubtitleStream integer,"
108                 "SubtitleDelay float, SubtitlesOn bool, Brightness float, Contrast float, Gamma float,"
109                 "VolumeAmplification float, AudioDelay float, OutputToAllSpeakers bool, ResumeTime integer, Crop bool, CropLeft integer,"
110                 "CropRight integer, CropTop integer, CropBottom integer, Sharpness float, NoiseReduction float, NonLinStretch bool, PostProcess bool,"
111                 "ScalingMethod integer, DeinterlaceMode integer, StereoMode integer, StereoInvert bool)\n");
112     m_pDS->exec("CREATE UNIQUE INDEX ix_settings ON settings ( idFile )\n");
113
114     CLog::Log(LOGINFO, "create stacktimes table");
115     m_pDS->exec("CREATE TABLE stacktimes (idFile integer, times text)\n");
116     m_pDS->exec("CREATE UNIQUE INDEX ix_stacktimes ON stacktimes ( idFile )\n");
117
118     CLog::Log(LOGINFO, "create genre table");
119     m_pDS->exec("CREATE TABLE genre ( idGenre integer primary key, strGenre text)\n");
120
121     CLog::Log(LOGINFO, "create genrelinkmovie table");
122     m_pDS->exec("CREATE TABLE genrelinkmovie ( idGenre integer, idMovie integer)\n");
123     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_1 ON genrelinkmovie ( idGenre, idMovie)\n");
124     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_2 ON genrelinkmovie ( idMovie, idGenre)\n");
125
126     CLog::Log(LOGINFO, "create country table");
127     m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
128
129     CLog::Log(LOGINFO, "create countrylinkmovie table");
130     m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
131     m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
132     m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
133
134     CLog::Log(LOGINFO, "create movie table");
135     CStdString columns = "CREATE TABLE movie ( idMovie integer primary key, idFile integer";
136
137     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
138       columns += StringUtils::Format(",c%02d text", i);
139
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)");
144
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");
149
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");
154
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");
159
160     CLog::Log(LOGINFO, "create actors table");
161     m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
162
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) )");
166
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) )");
170
171     CLog::Log(LOGINFO, "create tvshow table");
172     columns = "CREATE TABLE tvshow ( idShow integer primary key";
173
174     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
175       columns += StringUtils::Format(",c%02d text", i);;
176
177     columns += ")";
178     m_pDS->exec(columns.c_str());
179
180     CLog::Log(LOGINFO, "create directorlinktvshow table");
181     m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
182     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
183     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
184
185     CLog::Log(LOGINFO, "create actorlinktvshow table");
186     m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text, iOrder integer)\n");
187     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
188     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
189
190     CLog::Log(LOGINFO, "create studiolinktvshow table");
191     m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
192     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
193     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
194
195     CLog::Log(LOGINFO, "create episode table");
196     columns = "CREATE TABLE episode ( idEpisode integer primary key, idFile integer";
197     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
198     {
199       CStdString column;
200       if ( i == VIDEODB_ID_EPISODE_SEASON || i == VIDEODB_ID_EPISODE_EPISODE || i == VIDEODB_ID_EPISODE_BOOKMARK)
201         column = StringUtils::Format(",c%02d varchar(24)", i);
202       else
203         column = StringUtils::Format(",c%02d text", i);
204
205       columns += column;
206     }
207     columns += ", idShow integer)";
208     m_pDS->exec(columns.c_str());
209     m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
210     m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
211     CStdString createColIndex = StringUtils::Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
212     m_pDS->exec(createColIndex.c_str());
213     createColIndex = StringUtils::Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
214     m_pDS->exec(createColIndex.c_str());
215     m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
216     m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
217
218     CLog::Log(LOGINFO, "create tvshowlinkpath table");
219     m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
220     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
221     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
222
223     CLog::Log(LOGINFO, "create actorlinkepisode table");
224     m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text, iOrder integer)\n");
225     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
226     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
227
228     CLog::Log(LOGINFO, "create directorlinkepisode table");
229     m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
230     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
231     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
232
233     CLog::Log(LOGINFO, "create writerlinkepisode table");
234     m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
235     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
236     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
237
238     CLog::Log(LOGINFO, "create genrelinktvshow table");
239     m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
240     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
241     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
242
243     CLog::Log(LOGINFO, "create movielinktvshow table");
244     m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
245     m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
246     m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
247
248     CLog::Log(LOGINFO, "create studio table");
249     m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
250
251     CLog::Log(LOGINFO, "create studiolinkmovie table");
252     m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
253     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
254     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
255
256     CLog::Log(LOGINFO, "create musicvideo table");
257     columns = "CREATE TABLE musicvideo ( idMVideo integer primary key, idFile integer";
258     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
259       columns += StringUtils::Format(",c%02d text", i);;
260
261     columns += ")";
262     m_pDS->exec(columns.c_str());
263
264     m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
265     m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
266
267     CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
268     m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
269     m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
270     m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
271
272     CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
273     m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
274     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
275     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
276
277     CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
278     m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
279     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
280     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
281
282     CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
283     m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
284     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
285     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
286
287     CLog::Log(LOGINFO, "create streaminfo table");
288     m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
289       "strVideoCodec text, fVideoAspect float, iVideoWidth integer, iVideoHeight integer, "
290       "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text, iVideoDuration integer, strStereoMode text)");
291     m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
292
293    CLog::Log(LOGINFO, "create sets table");
294     m_pDS->exec("CREATE TABLE sets ( idSet integer primary key, strSet text)\n");
295
296     // create basepath indices
297     m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
298     m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
299     m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
300     m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c17(12) )");
301
302     CLog::Log(LOGINFO, "create seasons table");
303     m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
304     m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
305
306     CLog::Log(LOGINFO, "create art table");
307     m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
308     m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
309
310     CLog::Log(LOGINFO, "create tag table");
311     m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
312     m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
313
314     CLog::Log(LOGINFO, "create taglinks table");
315     m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
316     m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
317     m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
318     m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
319
320     CLog::Log(LOGINFO, "create deletion triggers");
321     m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
322                 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
323                 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
324                 "END");
325     m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
326                 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
327                 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
328                 "END");
329     m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
330                 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
331                 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
332                 "END");
333     m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
334                 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
335                 "END");
336     m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
337                 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
338                 "END");
339     m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
340                 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
341                 "END");
342     m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
343                 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
344                 "END");
345     m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
346                 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
347                 "END");
348
349     // we create views last to ensure all indexes are rolled in
350     CreateViews();
351   }
352   catch (...)
353   {
354     CLog::Log(LOGERROR, "%s unable to create tables:%i", __FUNCTION__, (int)GetLastError());
355     RollbackTransaction();
356     return false;
357   }
358   CommitTransaction();
359   return true;
360 }
361
362 void CVideoDatabase::CreateViews()
363 {
364   CLog::Log(LOGINFO, "create episodeview");
365   m_pDS->exec("DROP VIEW IF EXISTS episodeview");
366   CStdString episodeview = PrepareSQL("CREATE VIEW episodeview AS SELECT "
367                                       "  episode.*,"
368                                       "  files.strFileName AS strFileName,"
369                                       "  path.strPath AS strPath,"
370                                       "  files.playCount AS playCount,"
371                                       "  files.lastPlayed AS lastPlayed,"
372                                       "  files.dateAdded AS dateAdded,"
373                                       "  tvshow.c%02d AS strTitle,"
374                                       "  tvshow.c%02d AS strStudio,"
375                                       "  tvshow.c%02d AS premiered,"
376                                       "  tvshow.c%02d AS mpaa,"
377                                       "  tvshow.c%02d AS strShowPath, "
378                                       "  bookmark.timeInSeconds AS resumeTimeInSeconds, "
379                                       "  bookmark.totalTimeInSeconds AS totalTimeInSeconds, "
380                                       "  seasons.idSeason AS idSeason "
381                                       "FROM episode"
382                                       "  JOIN files ON"
383                                       "    files.idFile=episode.idFile"
384                                       "  JOIN tvshow ON"
385                                       "    tvshow.idShow=episode.idShow"
386                                       "  LEFT JOIN seasons ON"
387                                       "    seasons.idShow=episode.idShow AND seasons.season=episode.c%02d"
388                                       "  JOIN path ON"
389                                       "    files.idPath=path.idPath"
390                                       "  LEFT JOIN bookmark ON"
391                                       "    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);
392   m_pDS->exec(episodeview.c_str());
393
394   CLog::Log(LOGINFO, "create tvshowview");
395   m_pDS->exec("DROP VIEW IF EXISTS tvshowview");
396   CStdString tvshowview = PrepareSQL("CREATE VIEW tvshowview AS SELECT "
397                                      "  tvshow.*,"
398                                      "  path.strPath AS strPath,"
399                                      "  path.dateAdded AS dateAdded,"
400                                      "  MAX(files.lastPlayed) AS lastPlayed,"
401                                      "  NULLIF(COUNT(episode.c12), 0) AS totalCount,"
402                                      "  COUNT(files.playCount) AS watchedcount,"
403                                      "  NULLIF(COUNT(DISTINCT(episode.c12)), 0) AS totalSeasons "
404                                      "FROM tvshow"
405                                      "  LEFT JOIN tvshowlinkpath ON"
406                                      "    tvshowlinkpath.idShow=tvshow.idShow"
407                                      "  LEFT JOIN path ON"
408                                      "    path.idPath=tvshowlinkpath.idPath"
409                                      "  LEFT JOIN episode ON"
410                                      "    episode.idShow=tvshow.idShow"
411                                      "  LEFT JOIN files ON"
412                                      "    files.idFile=episode.idFile "
413                                      "GROUP BY tvshow.idShow;");
414   m_pDS->exec(tvshowview.c_str());
415
416   CLog::Log(LOGINFO, "create musicvideoview");
417   m_pDS->exec("DROP VIEW IF EXISTS musicvideoview");
418   m_pDS->exec("CREATE VIEW musicvideoview AS SELECT"
419               "  musicvideo.*,"
420               "  files.strFileName as strFileName,"
421               "  path.strPath as strPath,"
422               "  files.playCount as playCount,"
423               "  files.lastPlayed as lastPlayed,"
424               "  files.dateAdded as dateAdded, "
425               "  bookmark.timeInSeconds AS resumeTimeInSeconds, "
426               "  bookmark.totalTimeInSeconds AS totalTimeInSeconds "
427               "FROM musicvideo"
428               "  JOIN files ON"
429               "    files.idFile=musicvideo.idFile"
430               "  JOIN path ON"
431               "    path.idPath=files.idPath"
432               "  LEFT JOIN bookmark ON"
433               "    bookmark.idFile=musicvideo.idFile AND bookmark.type=1");
434
435   CLog::Log(LOGINFO, "create movieview");
436   m_pDS->exec("DROP VIEW IF EXISTS movieview");
437   m_pDS->exec("CREATE VIEW movieview AS SELECT"
438               "  movie.*,"
439               "  sets.strSet AS strSet,"
440               "  files.strFileName AS strFileName,"
441               "  path.strPath AS strPath,"
442               "  files.playCount AS playCount,"
443               "  files.lastPlayed AS lastPlayed, "
444               "  files.dateAdded AS dateAdded, "
445               "  bookmark.timeInSeconds AS resumeTimeInSeconds, "
446               "  bookmark.totalTimeInSeconds AS totalTimeInSeconds "
447               "FROM movie"
448               "  LEFT JOIN sets ON"
449               "    sets.idSet = movie.idSet"
450               "  JOIN files ON"
451               "    files.idFile=movie.idFile"
452               "  JOIN path ON"
453               "    path.idPath=files.idPath"
454               "  LEFT JOIN bookmark ON"
455               "    bookmark.idFile=movie.idFile AND bookmark.type=1");
456 }
457
458 //********************************************************************************************************************************
459 int CVideoDatabase::GetPathId(const CStdString& strPath)
460 {
461   CStdString strSQL;
462   try
463   {
464     int idPath=-1;
465     if (NULL == m_pDB.get()) return -1;
466     if (NULL == m_pDS.get()) return -1;
467
468     CStdString strPath1(strPath);
469     if (URIUtils::IsStack(strPath) || StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://"))
470       URIUtils::GetParentPath(strPath,strPath1);
471
472     URIUtils::AddSlashAtEnd(strPath1);
473
474     strSQL=PrepareSQL("select idPath from path where strPath='%s'",strPath1.c_str());
475     m_pDS->query(strSQL.c_str());
476     if (!m_pDS->eof())
477       idPath = m_pDS->fv("path.idPath").get_asInt();
478
479     m_pDS->close();
480     return idPath;
481   }
482   catch (...)
483   {
484     CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
485   }
486   return -1;
487 }
488
489 bool CVideoDatabase::GetPaths(set<CStdString> &paths)
490 {
491   try
492   {
493     if (NULL == m_pDB.get()) return false;
494     if (NULL == m_pDS.get()) return false;
495
496     paths.clear();
497
498     // grab all paths with movie content set
499     if (!m_pDS->query("select strPath,noUpdate from path"
500                       " where (strContent = 'movies' or strContent = 'musicvideos')"
501                       " and strPath NOT like 'multipath://%%'"
502                       " order by strPath"))
503       return false;
504
505     while (!m_pDS->eof())
506     {
507       if (!m_pDS->fv("noUpdate").get_asBool())
508         paths.insert(m_pDS->fv("strPath").get_asString());
509       m_pDS->next();
510     }
511     m_pDS->close();
512
513     // then grab all tvshow paths
514     if (!m_pDS->query("select strPath,noUpdate from path"
515                       " where ( strContent = 'tvshows'"
516                       "       or idPath in (select idPath from tvshowlinkpath))"
517                       " and strPath NOT like 'multipath://%%'"
518                       " order by strPath"))
519       return false;
520
521     while (!m_pDS->eof())
522     {
523       if (!m_pDS->fv("noUpdate").get_asBool())
524         paths.insert(m_pDS->fv("strPath").get_asString());
525       m_pDS->next();
526     }
527     m_pDS->close();
528
529     // finally grab all other paths holding a movie which is not a stack or a rar archive
530     // - this isnt perfect but it should do fine in most situations.
531     // reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
532     // not making mistakes must take priority
533     if (!m_pDS->query("select strPath,noUpdate from path"
534                        " where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
535                        " and idPath NOT in (select idPath from tvshowlinkpath)"
536                        " 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
537                        " and idPath NOT in (select idPath from files where strFileName like 'index.bdmv')" // bluray folders get stacked to a single item in parent folder
538                        " and strPath NOT like 'multipath://%%'"
539                        " and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
540                        " order by strPath"))
541
542       return false;
543     while (!m_pDS->eof())
544     {
545       if (!m_pDS->fv("noUpdate").get_asBool())
546         paths.insert(m_pDS->fv("strPath").get_asString());
547       m_pDS->next();
548     }
549     m_pDS->close();
550     return true;
551   }
552   catch (...)
553   {
554     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
555   }
556   return false;
557 }
558
559 bool CVideoDatabase::GetPathsForTvShow(int idShow, set<int>& paths)
560 {
561   CStdString strSQL;
562   try
563   {
564     if (NULL == m_pDB.get()) return false;
565     if (NULL == m_pDS.get()) return false;
566     strSQL = PrepareSQL("SELECT DISTINCT idPath FROM files JOIN episode ON episode.idFile=files.idFile WHERE episode.idShow=%i",idShow);
567     m_pDS->query(strSQL.c_str());
568     while (!m_pDS->eof())
569     {
570       paths.insert(m_pDS->fv(0).get_asInt());
571       m_pDS->next();
572     }
573     m_pDS->close();
574     return true;
575   }
576   catch (...)
577   {
578     CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
579   }
580   return false;
581 }
582
583 int CVideoDatabase::RunQuery(const CStdString &sql)
584 {
585   unsigned int time = XbmcThreads::SystemClockMillis();
586   int rows = -1;
587   if (m_pDS->query(sql.c_str()))
588   {
589     rows = m_pDS->num_rows();
590     if (rows == 0)
591       m_pDS->close();
592   }
593   CLog::Log(LOGDEBUG, "%s took %d ms for %d items query: %s", __FUNCTION__, XbmcThreads::SystemClockMillis() - time, rows, sql.c_str());
594   return rows;
595 }
596
597 bool CVideoDatabase::GetSubPaths(const CStdString &basepath, vector< pair<int,string> >& subpaths)
598 {
599   CStdString sql;
600   try
601   {
602     if (!m_pDB.get() || !m_pDS.get())
603       return false;
604
605     CStdString path(basepath);
606     URIUtils::AddSlashAtEnd(path);
607     sql = PrepareSQL("SELECT idPath,strPath FROM path WHERE SUBSTR(strPath,1,%i)='%s'", StringUtils::utf8_strlen(path.c_str()), path.c_str());
608     m_pDS->query(sql.c_str());
609     while (!m_pDS->eof())
610     {
611       subpaths.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
612       m_pDS->next();
613     }
614     m_pDS->close();
615     return true;
616   }
617   catch (...)
618   {
619     CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, sql.c_str());
620   }
621   return false;
622 }
623
624 int CVideoDatabase::AddPath(const CStdString& strPath, const CStdString &strDateAdded /*= "" */)
625 {
626   CStdString strSQL;
627   try
628   {
629     int idPath = GetPathId(strPath);
630     if (idPath >= 0)
631       return idPath; // already have the path
632
633     if (NULL == m_pDB.get()) return -1;
634     if (NULL == m_pDS.get()) return -1;
635
636     CStdString strPath1(strPath);
637     if (URIUtils::IsStack(strPath) || StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://"))
638       URIUtils::GetParentPath(strPath,strPath1);
639
640     URIUtils::AddSlashAtEnd(strPath1);
641
642     // only set dateadded if we got one
643     if (!strDateAdded.empty())
644       strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper, dateAdded) values (NULL,'%s','','', '%s')", strPath1.c_str(), strDateAdded.c_str());
645     else
646       strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
647     m_pDS->exec(strSQL.c_str());
648     idPath = (int)m_pDS->lastinsertid();
649     return idPath;
650   }
651   catch (...)
652   {
653     CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
654   }
655   return -1;
656 }
657
658 bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
659 {
660   try
661   {
662     if (NULL == m_pDB.get()) return false;
663     if (NULL == m_pDS.get()) return false;
664
665     CStdString strSQL=PrepareSQL("select strHash from path where strPath='%s'", path.c_str());
666     m_pDS->query(strSQL.c_str());
667     if (m_pDS->num_rows() == 0)
668       return false;
669     hash = m_pDS->fv("strHash").get_asString();
670     return true;
671   }
672   catch (...)
673   {
674     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
675   }
676
677   return false;
678 }
679
680 //********************************************************************************************************************************
681 int CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
682 {
683   CStdString strSQL = "";
684   try
685   {
686     int idFile;
687     if (NULL == m_pDB.get()) return -1;
688     if (NULL == m_pDS.get()) return -1;
689
690     CStdString strFileName, strPath;
691     SplitPath(strFileNameAndPath,strPath,strFileName);
692
693     int idPath = AddPath(strPath);
694     if (idPath < 0)
695       return -1;
696
697     CStdString strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
698
699     m_pDS->query(strSQL.c_str());
700     if (m_pDS->num_rows() > 0)
701     {
702       idFile = m_pDS->fv("idFile").get_asInt() ;
703       m_pDS->close();
704       return idFile;
705     }
706     m_pDS->close();
707
708     strSQL=PrepareSQL("insert into files (idFile, idPath, strFileName) values(NULL, %i, '%s')", idPath, strFileName.c_str());
709     m_pDS->exec(strSQL.c_str());
710     idFile = (int)m_pDS->lastinsertid();
711     return idFile;
712   }
713   catch (...)
714   {
715     CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
716   }
717   return -1;
718 }
719
720 int CVideoDatabase::AddFile(const CFileItem& item)
721 {
722   if (item.IsVideoDb() && item.HasVideoInfoTag())
723     return AddFile(item.GetVideoInfoTag()->m_strFileNameAndPath);
724   return AddFile(item.GetPath());
725 }
726
727 void CVideoDatabase::UpdateFileDateAdded(int idFile, const CStdString& strFileNameAndPath)
728 {
729   if (idFile < 0 || strFileNameAndPath.empty())
730     return;
731     
732   CStdString strSQL = "";
733   try
734   {
735     if (NULL == m_pDB.get()) return;
736     if (NULL == m_pDS.get()) return;
737
738     CStdString file = strFileNameAndPath;
739     if (URIUtils::IsStack(strFileNameAndPath))
740       file = CStackDirectory::GetFirstStackedFile(strFileNameAndPath);
741
742     if (URIUtils::IsInArchive(file))
743       file = CURL(file).GetHostName();
744
745     CDateTime dateAdded;
746     // Skip looking at the files ctime/mtime if defined by the user through as.xml
747     if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
748     {
749       // Let's try to get the modification datetime
750       struct __stat64 buffer;
751       if (CFile::Stat(file, &buffer) == 0 && (buffer.st_mtime != 0 || buffer.st_ctime !=0))
752       {
753         time_t now = time(NULL);
754         time_t addedTime;
755         // Prefer the modification time if it's valid
756         if (g_advancedSettings.m_iVideoLibraryDateAdded == 1)
757         {
758           if (buffer.st_mtime != 0 && (time_t)buffer.st_mtime <= now)
759             addedTime = (time_t)buffer.st_mtime;
760           else
761             addedTime = (time_t)buffer.st_ctime;
762         }
763         // Use the newer of the creation and modification time
764         else
765         {
766           addedTime = max((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
767           // if the newer of the two dates is in the future, we try it with the older one
768           if (addedTime > now)
769             addedTime = min((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
770         }
771
772         // make sure the datetime does is not in the future
773         if (addedTime <= now)
774         {
775           struct tm *time = localtime(&addedTime);
776           if (time)
777             dateAdded = *time;
778         }
779       }
780     }
781
782     if (!dateAdded.IsValid())
783       dateAdded = CDateTime::GetCurrentDateTime();
784
785     strSQL = PrepareSQL("update files set dateAdded='%s' where idFile=%d", dateAdded.GetAsDBDateTime().c_str(), idFile);
786     m_pDS->exec(strSQL.c_str());
787   }
788   catch (...)
789   {
790     CLog::Log(LOGERROR, "%s unable to update dateadded for file (%s)", __FUNCTION__, strSQL.c_str());
791   }
792 }
793
794 bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
795 {
796   try
797   {
798     if (NULL == m_pDB.get()) return false;
799     if (NULL == m_pDS.get()) return false;
800
801     if (hash.empty())
802     { // this is an empty folder - we need only add it to the path table
803       // if the path actually exists
804       if (!CDirectory::Exists(path))
805         return false;
806     }
807     int idPath = AddPath(path);
808     if (idPath < 0) return false;
809
810     CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
811     m_pDS->exec(strSQL.c_str());
812
813     return true;
814   }
815   catch (...)
816   {
817     CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
818   }
819
820   return false;
821 }
822
823 bool CVideoDatabase::LinkMovieToTvshow(int idMovie, int idShow, bool bRemove)
824 {
825    try
826   {
827     if (NULL == m_pDB.get()) return false;
828     if (NULL == m_pDS.get()) return false;
829
830     if (bRemove) // delete link
831     {
832       CStdString strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i and idShow=%i", idMovie, idShow);
833       m_pDS->exec(strSQL.c_str());
834       return true;
835     }
836
837     CStdString strSQL=PrepareSQL("insert into movielinktvshow (idShow,idMovie) values (%i,%i)", idShow,idMovie);
838     m_pDS->exec(strSQL.c_str());
839
840     return true;
841   }
842   catch (...)
843   {
844     CLog::Log(LOGERROR, "%s (%i, %i) failed", __FUNCTION__, idMovie, idShow);
845   }
846
847   return false;
848 }
849
850 bool CVideoDatabase::IsLinkedToTvshow(int idMovie)
851 {
852    try
853   {
854     if (NULL == m_pDB.get()) return false;
855     if (NULL == m_pDS.get()) return false;
856
857     CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
858     m_pDS->query(strSQL.c_str());
859     if (m_pDS->eof())
860     {
861       m_pDS->close();
862       return false;
863     }
864
865     m_pDS->close();
866     return true;
867   }
868   catch (...)
869   {
870     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
871   }
872
873   return false;
874 }
875
876 bool CVideoDatabase::GetLinksToTvShow(int idMovie, vector<int>& ids)
877 {
878    try
879   {
880     if (NULL == m_pDB.get()) return false;
881     if (NULL == m_pDS.get()) return false;
882
883     CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
884     m_pDS2->query(strSQL.c_str());
885     while (!m_pDS2->eof())
886     {
887       ids.push_back(m_pDS2->fv(1).get_asInt());
888       m_pDS2->next();
889     }
890
891     m_pDS2->close();
892     return true;
893   }
894   catch (...)
895   {
896     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
897   }
898
899   return false;
900 }
901
902
903 //********************************************************************************************************************************
904 int CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
905 {
906   try
907   {
908     if (NULL == m_pDB.get()) return -1;
909     if (NULL == m_pDS.get()) return -1;
910     CStdString strPath, strFileName;
911     SplitPath(strFilenameAndPath,strPath,strFileName);
912
913     int idPath = GetPathId(strPath);
914     if (idPath >= 0)
915     {
916       CStdString strSQL;
917       strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
918       m_pDS->query(strSQL.c_str());
919       if (m_pDS->num_rows() > 0)
920       {
921         int idFile = m_pDS->fv("files.idFile").get_asInt();
922         m_pDS->close();
923         return idFile;
924       }
925     }
926   }
927   catch (...)
928   {
929     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
930   }
931   return -1;
932 }
933
934 int CVideoDatabase::GetFileId(const CFileItem &item)
935 {
936   if (item.IsVideoDb() && item.HasVideoInfoTag())
937     return GetFileId(item.GetVideoInfoTag()->m_strFileNameAndPath);
938   return GetFileId(item.GetPath());
939 }
940
941 //********************************************************************************************************************************
942 int CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
943 {
944   try
945   {
946     if (NULL == m_pDB.get()) return -1;
947     if (NULL == m_pDS.get()) return -1;
948     int idMovie = -1;
949
950     // needed for query parameters
951     int idFile = GetFileId(strFilenameAndPath);
952     int idPath=-1;
953     CStdString strPath;
954     if (idFile < 0)
955     {
956       CStdString strFile;
957       SplitPath(strFilenameAndPath,strPath,strFile);
958
959       // have to join movieinfo table for correct results
960       idPath = GetPathId(strPath);
961       if (idPath < 0 && strPath != strFilenameAndPath)
962         return -1;
963     }
964
965     if (idFile == -1 && strPath != strFilenameAndPath)
966       return -1;
967
968     CStdString strSQL;
969     if (idFile == -1)
970       strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
971     else
972       strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
973
974     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
975     m_pDS->query(strSQL.c_str());
976     if (m_pDS->num_rows() > 0)
977       idMovie = m_pDS->fv("idMovie").get_asInt();
978     m_pDS->close();
979
980     return idMovie;
981   }
982   catch (...)
983   {
984     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
985   }
986   return -1;
987 }
988
989 int CVideoDatabase::GetTvShowId(const CStdString& strPath)
990 {
991   try
992   {
993     if (NULL == m_pDB.get()) return -1;
994     if (NULL == m_pDS.get()) return -1;
995     int idTvShow = -1;
996
997     // have to join movieinfo table for correct results
998     int idPath = GetPathId(strPath);
999     if (idPath < 0)
1000       return -1;
1001
1002     CStdString strSQL;
1003     CStdString strPath1=strPath;
1004     CStdString strParent;
1005     int iFound=0;
1006
1007     strSQL=PrepareSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%i",idPath);
1008     m_pDS->query(strSQL);
1009     if (!m_pDS->eof())
1010       iFound = 1;
1011
1012     while (iFound == 0 && URIUtils::GetParentPath(strPath1, strParent))
1013     {
1014       strSQL=PrepareSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and strPath='%s'",strParent.c_str());
1015       m_pDS->query(strSQL.c_str());
1016       if (!m_pDS->eof())
1017       {
1018         int idShow = m_pDS->fv("idShow").get_asInt();
1019         if (idShow != -1)
1020           iFound = 2;
1021       }
1022       strPath1 = strParent;
1023     }
1024
1025     if (m_pDS->num_rows() > 0)
1026       idTvShow = m_pDS->fv("idShow").get_asInt();
1027     m_pDS->close();
1028
1029     return idTvShow;
1030   }
1031   catch (...)
1032   {
1033     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1034   }
1035   return -1;
1036 }
1037
1038 int CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, int idEpisode, int idSeason) // input value is episode/season number hint - for multiparters
1039 {
1040   try
1041   {
1042     if (NULL == m_pDB.get()) return -1;
1043     if (NULL == m_pDS.get()) return -1;
1044
1045     // need this due to the nested GetEpisodeInfo query
1046     auto_ptr<Dataset> pDS;
1047     pDS.reset(m_pDB->CreateDataset());
1048     if (NULL == pDS.get()) return -1;
1049
1050     int idFile = GetFileId(strFilenameAndPath);
1051     if (idFile < 0)
1052       return -1;
1053
1054     CStdString strSQL=PrepareSQL("select idEpisode from episode where idFile=%i", idFile);
1055
1056     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
1057     pDS->query(strSQL.c_str());
1058     if (pDS->num_rows() > 0)
1059     {
1060       if (idEpisode == -1)
1061         idEpisode = pDS->fv("episode.idEpisode").get_asInt();
1062       else // use the hint!
1063       {
1064         while (!pDS->eof())
1065         {
1066           CVideoInfoTag tag;
1067           int idTmpEpisode = pDS->fv("episode.idEpisode").get_asInt();
1068           GetEpisodeInfo(strFilenameAndPath,tag,idTmpEpisode);
1069           if (tag.m_iEpisode == idEpisode && (idSeason == -1 || tag.m_iSeason == idSeason)) {
1070             // match on the episode hint, and there's no season hint or a season hint match
1071             idEpisode = idTmpEpisode;
1072             break;
1073           }
1074           pDS->next();
1075         }
1076         if (pDS->eof())
1077           idEpisode = -1;
1078       }
1079     }
1080     else
1081       idEpisode = -1;
1082
1083     pDS->close();
1084
1085     return idEpisode;
1086   }
1087   catch (...)
1088   {
1089     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1090   }
1091   return -1;
1092 }
1093
1094 int CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
1095 {
1096   try
1097   {
1098     if (NULL == m_pDB.get()) return -1;
1099     if (NULL == m_pDS.get()) return -1;
1100
1101     int idFile = GetFileId(strFilenameAndPath);
1102     if (idFile < 0)
1103       return -1;
1104
1105     CStdString strSQL=PrepareSQL("select idMVideo from musicvideo where idFile=%i", idFile);
1106
1107     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
1108     m_pDS->query(strSQL.c_str());
1109     int idMVideo=-1;
1110     if (m_pDS->num_rows() > 0)
1111       idMVideo = m_pDS->fv("idMVideo").get_asInt();
1112     m_pDS->close();
1113
1114     return idMVideo;
1115   }
1116   catch (...)
1117   {
1118     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1119   }
1120   return -1;
1121 }
1122
1123 //********************************************************************************************************************************
1124 int CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
1125 {
1126   try
1127   {
1128     if (NULL == m_pDB.get()) return -1;
1129     if (NULL == m_pDS.get()) return -1;
1130
1131     int idMovie = GetMovieId(strFilenameAndPath);
1132     if (idMovie < 0)
1133     {
1134       int idFile = AddFile(strFilenameAndPath);
1135       if (idFile < 0)
1136         return -1;
1137       UpdateFileDateAdded(idFile, strFilenameAndPath);
1138       CStdString strSQL=PrepareSQL("insert into movie (idMovie, idFile) values (NULL, %i)", idFile);
1139       m_pDS->exec(strSQL.c_str());
1140       idMovie = (int)m_pDS->lastinsertid();
1141     }
1142
1143     return idMovie;
1144   }
1145   catch (...)
1146   {
1147     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1148   }
1149   return -1;
1150 }
1151
1152 int CVideoDatabase::AddTvShow(const CStdString& strPath)
1153 {
1154   try
1155   {
1156     if (NULL == m_pDB.get()) return -1;
1157     if (NULL == m_pDS.get()) return -1;
1158
1159     CStdString strSQL=PrepareSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath='%s' and path.idPath=tvshowlinkpath.idPath",strPath.c_str());
1160     m_pDS->query(strSQL.c_str());
1161     if (m_pDS->num_rows() != 0)
1162       return m_pDS->fv("tvshowlinkpath.idShow").get_asInt();
1163
1164     strSQL=PrepareSQL("insert into tvshow (idShow) values (NULL)");
1165     m_pDS->exec(strSQL.c_str());
1166     int idTvShow = (int)m_pDS->lastinsertid();
1167
1168     // Get the creation datetime of the tvshow directory
1169     CDateTime dateAdded;
1170     // Skip looking at the files ctime/mtime if defined by the user through as.xml
1171     if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
1172     {
1173       struct __stat64 buffer;
1174       if (XFILE::CFile::Stat(strPath, &buffer) == 0)
1175       {
1176         time_t now = time(NULL);
1177         // Make sure we have a valid date (i.e. not in the future)
1178         if ((time_t)buffer.st_ctime <= now)
1179         {
1180           struct tm *time = localtime((const time_t*)&buffer.st_ctime);
1181           if (time)
1182             dateAdded = *time;
1183         }
1184       }
1185     }
1186
1187     if (!dateAdded.IsValid())
1188       dateAdded = CDateTime::GetCurrentDateTime();
1189
1190     int idPath = AddPath(strPath, dateAdded.GetAsDBDateTime());
1191     strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
1192     m_pDS->exec(strSQL.c_str());
1193
1194     return idTvShow;
1195   }
1196   catch (...)
1197   {
1198     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1199   }
1200   return -1;
1201 }
1202
1203 //********************************************************************************************************************************
1204 int CVideoDatabase::AddEpisode(int idShow, const CStdString& strFilenameAndPath)
1205 {
1206   try
1207   {
1208     if (NULL == m_pDB.get()) return -1;
1209     if (NULL == m_pDS.get()) return -1;
1210
1211     int idFile = AddFile(strFilenameAndPath);
1212     if (idFile < 0)
1213       return -1;
1214     UpdateFileDateAdded(idFile, strFilenameAndPath);
1215
1216     CStdString strSQL=PrepareSQL("insert into episode (idEpisode, idFile, idShow) values (NULL, %i, %i)", idFile, idShow);
1217     m_pDS->exec(strSQL.c_str());
1218     return (int)m_pDS->lastinsertid();
1219   }
1220   catch (...)
1221   {
1222     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1223   }
1224   return -1;
1225 }
1226
1227 int CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
1228 {
1229   try
1230   {
1231     if (NULL == m_pDB.get()) return -1;
1232     if (NULL == m_pDS.get()) return -1;
1233
1234     int idMVideo = GetMusicVideoId(strFilenameAndPath);
1235     if (idMVideo < 0)
1236     {
1237       int idFile = AddFile(strFilenameAndPath);
1238       if (idFile < 0)
1239         return -1;
1240       UpdateFileDateAdded(idFile, strFilenameAndPath);
1241       CStdString strSQL=PrepareSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %i)", idFile);
1242       m_pDS->exec(strSQL.c_str());
1243       idMVideo = (int)m_pDS->lastinsertid();
1244     }
1245
1246     return idMVideo;
1247   }
1248   catch (...)
1249   {
1250     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1251   }
1252   return -1;
1253 }
1254
1255 //********************************************************************************************************************************
1256 int CVideoDatabase::AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value)
1257 {
1258   try
1259   {
1260     if (NULL == m_pDB.get()) return -1;
1261     if (NULL == m_pDS.get()) return -1;
1262
1263     CStdString strSQL = PrepareSQL("select %s from %s where %s like '%s'", firstField.c_str(), table.c_str(), secondField.c_str(), value.c_str());
1264     m_pDS->query(strSQL.c_str());
1265     if (m_pDS->num_rows() == 0)
1266     {
1267       m_pDS->close();
1268       // doesnt exists, add it
1269       strSQL = PrepareSQL("insert into %s (%s, %s) values(NULL, '%s')", table.c_str(), firstField.c_str(), secondField.c_str(), value.c_str());      
1270       m_pDS->exec(strSQL.c_str());
1271       int id = (int)m_pDS->lastinsertid();
1272       return id;
1273     }
1274     else
1275     {
1276       int id = m_pDS->fv(firstField).get_asInt();
1277       m_pDS->close();
1278       return id;
1279     }
1280   }
1281   catch (...)
1282   {
1283     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, value.c_str() );
1284   }
1285
1286   return -1;
1287 }
1288
1289 int CVideoDatabase::AddSet(const CStdString& strSet)
1290 {
1291   if (strSet.empty())
1292     return -1;
1293
1294   return AddToTable("sets", "idSet", "strSet", strSet);
1295 }
1296
1297 int CVideoDatabase::AddTag(const std::string& tag)
1298 {
1299   if (tag.empty())
1300     return -1;
1301
1302   return AddToTable("tag", "idTag", "strTag", tag);
1303 }
1304
1305 int CVideoDatabase::AddGenre(const CStdString& strGenre)
1306 {
1307   return AddToTable("genre", "idGenre", "strGenre", strGenre);
1308 }
1309
1310 int CVideoDatabase::AddStudio(const CStdString& strStudio)
1311 {
1312   return AddToTable("studio", "idStudio", "strStudio", strStudio);
1313 }
1314
1315 //********************************************************************************************************************************
1316 int CVideoDatabase::AddCountry(const CStdString& strCountry)
1317 {
1318   return AddToTable("country", "idCountry", "strCountry", strCountry);
1319 }
1320
1321 int CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& thumbURLs, const CStdString &thumb)
1322 {
1323   try
1324   {
1325     if (NULL == m_pDB.get()) return -1;
1326     if (NULL == m_pDS.get()) return -1;
1327     int idActor = -1;
1328     CStdString strSQL=PrepareSQL("select idActor from actors where strActor like '%s'", strActor.c_str());
1329     m_pDS->query(strSQL.c_str());
1330     if (m_pDS->num_rows() == 0)
1331     {
1332       m_pDS->close();
1333       // doesnt exists, add it
1334       strSQL=PrepareSQL("insert into actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),thumbURLs.c_str());
1335       m_pDS->exec(strSQL.c_str());
1336       idActor = (int)m_pDS->lastinsertid();
1337     }
1338     else
1339     {
1340       idActor = m_pDS->fv("idActor").get_asInt();
1341       m_pDS->close();
1342       // update the thumb url's
1343       if (!thumbURLs.empty())
1344       {
1345         strSQL=PrepareSQL("update actors set strThumb='%s' where idActor=%i",thumbURLs.c_str(),idActor);
1346         m_pDS->exec(strSQL.c_str());
1347       }
1348     }
1349     // add artwork
1350     if (!thumb.empty())
1351       SetArtForItem(idActor, "actor", "thumb", thumb);
1352     return idActor;
1353   }
1354   catch (...)
1355   {
1356     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
1357   }
1358   return -1;
1359 }
1360
1361
1362
1363 void CVideoDatabase::AddLinkToActor(const char *table, int actorID, const char *field, int secondID, const CStdString &role, int order)
1364 {
1365   try
1366   {
1367     if (NULL == m_pDB.get()) return ;
1368     if (NULL == m_pDS.get()) return ;
1369
1370     CStdString strSQL=PrepareSQL("select * from actorlink%s where idActor=%i and id%s=%i", table, actorID, field, secondID);
1371     m_pDS->query(strSQL.c_str());
1372     if (m_pDS->num_rows() == 0)
1373     {
1374       // doesnt exists, add it
1375       strSQL=PrepareSQL("insert into actorlink%s (idActor, id%s, strRole, iOrder) values(%i,%i,'%s',%i)", table, field, actorID, secondID, role.c_str(), order);
1376       m_pDS->exec(strSQL.c_str());
1377     }
1378     m_pDS->close();
1379   }
1380   catch (...)
1381   {
1382     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1383   }
1384 }
1385
1386 void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1387 {
1388   try
1389   {
1390     if (NULL == m_pDB.get()) return ;
1391     if (NULL == m_pDS.get()) return ;
1392
1393     CStdString strSQL = PrepareSQL("select * from %s where %s=%i and %s=%i", table, firstField, firstID, secondField, secondID);
1394     if (typeField != NULL && type != NULL)
1395       strSQL += PrepareSQL(" and %s='%s'", typeField, type);
1396     m_pDS->query(strSQL.c_str());
1397     if (m_pDS->num_rows() == 0)
1398     {
1399       // doesnt exists, add it
1400       if (typeField == NULL || type == NULL)
1401         strSQL = PrepareSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
1402       else
1403         strSQL = PrepareSQL("insert into %s (%s,%s,%s) values(%i,%i,'%s')", table, firstField, secondField, typeField, firstID, secondID, type);
1404       m_pDS->exec(strSQL.c_str());
1405     }
1406     m_pDS->close();
1407   }
1408   catch (...)
1409   {
1410     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1411   }
1412 }
1413
1414 void CVideoDatabase::RemoveFromLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1415 {
1416   try
1417   {
1418     if (NULL == m_pDB.get()) return ;
1419     if (NULL == m_pDS.get()) return ;
1420
1421     CStdString strSQL = PrepareSQL("DELETE FROM %s WHERE %s = %i AND %s = %i", table, firstField, firstID, secondField, secondID);
1422     if (typeField != NULL && type != NULL)
1423       strSQL += PrepareSQL(" AND %s='%s'", typeField, type);
1424     m_pDS->exec(strSQL.c_str());
1425   }
1426   catch (...)
1427   {
1428     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1429   }
1430 }
1431
1432 //****Tags****
1433 void CVideoDatabase::AddTagToItem(int idMovie, int idTag, const std::string &type)
1434 {
1435   if (type.empty())
1436     return;
1437
1438   AddToLinkTable("taglinks", "idTag", idTag, "idMedia", idMovie, "media_type", type.c_str());
1439 }
1440
1441 void CVideoDatabase::RemoveTagFromItem(int idItem, int idTag, const std::string &type)
1442 {
1443   if (type.empty())
1444     return;
1445
1446   RemoveFromLinkTable("taglinks", "idTag", idTag, "idMedia", idItem, "media_type", type.c_str());
1447 }
1448
1449 void CVideoDatabase::RemoveTagsFromItem(int idItem, const std::string &type)
1450 {
1451   if (type.empty())
1452     return;
1453
1454   m_pDS2->exec(PrepareSQL("DELETE FROM taglinks WHERE idMedia=%d AND media_type='%s'", idItem, type.c_str()));
1455 }
1456
1457 //****Actors****
1458 void CVideoDatabase::AddCast(int idMedia, const char *table, const char *field, const std::vector< SActorInfo > &cast)
1459 {
1460   if (cast.empty())
1461     return;
1462
1463   int order = std::max_element(cast.begin(), cast.end())->order;
1464   for (CVideoInfoTag::iCast it = cast.begin(); it != cast.end(); ++it)
1465   {
1466     int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
1467     AddLinkToActor(table, idActor, field, idMedia, it->strRole, it->order >= 0 ? it->order : ++order);
1468   }
1469 }
1470
1471 void CVideoDatabase::AddArtistToMusicVideo(int idMVideo, int idArtist)
1472 {
1473   AddToLinkTable("artistlinkmusicvideo", "idArtist", idArtist, "idMVideo", idMVideo);
1474 }
1475
1476 //****Directors + Writers****
1477 void CVideoDatabase::AddDirectorToMovie(int idMovie, int idDirector)
1478 {
1479   AddToLinkTable("directorlinkmovie", "idDirector", idDirector, "idMovie", idMovie);
1480 }
1481
1482 void CVideoDatabase::AddDirectorToTvShow(int idTvShow, int idDirector)
1483 {
1484   AddToLinkTable("directorlinktvshow", "idDirector", idDirector, "idShow", idTvShow);
1485 }
1486
1487 void CVideoDatabase::AddWriterToEpisode(int idEpisode, int idWriter)
1488 {
1489   AddToLinkTable("writerlinkepisode", "idWriter", idWriter, "idEpisode", idEpisode);
1490 }
1491
1492 void CVideoDatabase::AddWriterToMovie(int idMovie, int idWriter)
1493 {
1494   AddToLinkTable("writerlinkmovie", "idWriter", idWriter, "idMovie", idMovie);
1495 }
1496
1497 void CVideoDatabase::AddDirectorToEpisode(int idEpisode, int idDirector)
1498 {
1499   AddToLinkTable("directorlinkepisode", "idDirector", idDirector, "idEpisode", idEpisode);
1500 }
1501
1502 void CVideoDatabase::AddDirectorToMusicVideo(int idMVideo, int idDirector)
1503 {
1504   AddToLinkTable("directorlinkmusicvideo", "idDirector", idDirector, "idMVideo", idMVideo);
1505 }
1506
1507 //****Studios****
1508 void CVideoDatabase::AddStudioToMovie(int idMovie, int idStudio)
1509 {
1510   AddToLinkTable("studiolinkmovie", "idStudio", idStudio, "idMovie", idMovie);
1511 }
1512
1513 void CVideoDatabase::AddStudioToTvShow(int idTvShow, int idStudio)
1514 {
1515   AddToLinkTable("studiolinktvshow", "idStudio", idStudio, "idShow", idTvShow);
1516 }
1517
1518 void CVideoDatabase::AddStudioToMusicVideo(int idMVideo, int idStudio)
1519 {
1520   AddToLinkTable("studiolinkmusicvideo", "idStudio", idStudio, "idMVideo", idMVideo);
1521 }
1522
1523 //****Genres****
1524 void CVideoDatabase::AddGenreToMovie(int idMovie, int idGenre)
1525 {
1526   AddToLinkTable("genrelinkmovie", "idGenre", idGenre, "idMovie", idMovie);
1527 }
1528
1529 void CVideoDatabase::AddGenreToTvShow(int idTvShow, int idGenre)
1530 {
1531   AddToLinkTable("genrelinktvshow", "idGenre", idGenre, "idShow", idTvShow);
1532 }
1533
1534 void CVideoDatabase::AddGenreToMusicVideo(int idMVideo, int idGenre)
1535 {
1536   AddToLinkTable("genrelinkmusicvideo", "idGenre", idGenre, "idMVideo", idMVideo);
1537 }
1538
1539 //****Country****
1540 void CVideoDatabase::AddCountryToMovie(int idMovie, int idCountry)
1541 {
1542   AddToLinkTable("countrylinkmovie", "idCountry", idCountry, "idMovie", idMovie);
1543 }
1544
1545 //********************************************************************************************************************************
1546 bool CVideoDatabase::LoadVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details)
1547 {
1548   if (GetMovieInfo(strFilenameAndPath, details))
1549     return true;
1550   if (GetEpisodeInfo(strFilenameAndPath, details))
1551     return true;
1552   if (GetMusicVideoInfo(strFilenameAndPath, details))
1553     return true;
1554   if (GetFileInfo(strFilenameAndPath, details))
1555     return true;
1556
1557   return false;
1558 }
1559
1560 bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
1561 {
1562   try
1563   {
1564     if (NULL == m_pDB.get()) return false;
1565     if (NULL == m_pDS.get()) return false;
1566     int idMovie = GetMovieId(strFilenameAndPath);
1567     return (idMovie > 0); // index of zero is also invalid
1568   }
1569   catch (...)
1570   {
1571     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1572   }
1573   return false;
1574 }
1575
1576 bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
1577 {
1578   try
1579   {
1580     if (NULL == m_pDB.get()) return false;
1581     if (NULL == m_pDS.get()) return false;
1582     int idTvShow = GetTvShowId(strPath);
1583     return (idTvShow > 0); // index of zero is also invalid
1584   }
1585   catch (...)
1586   {
1587     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1588   }
1589   return false;
1590 }
1591
1592 bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
1593 {
1594   try
1595   {
1596     if (NULL == m_pDB.get()) return false;
1597     if (NULL == m_pDS.get()) return false;
1598     int idEpisode = GetEpisodeId(strFilenameAndPath);
1599     return (idEpisode > 0); // index of zero is also invalid
1600   }
1601   catch (...)
1602   {
1603     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1604   }
1605   return false;
1606 }
1607
1608 bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
1609 {
1610   try
1611   {
1612     if (NULL == m_pDB.get()) return false;
1613     if (NULL == m_pDS.get()) return false;
1614     int idMVideo = GetMusicVideoId(strFilenameAndPath);
1615     return (idMVideo > 0); // index of zero is also invalid
1616   }
1617   catch (...)
1618   {
1619     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1620   }
1621   return false;
1622 }
1623
1624 void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath, int idTvShow /* = -1 */)
1625 {
1626   try
1627   {
1628     if (NULL == m_pDB.get()) return ;
1629     if (NULL == m_pDS.get()) return ;
1630
1631     if (idTvShow < 0)
1632     {
1633       idTvShow = GetTvShowId(strPath);
1634       if (idTvShow < 0)
1635         return;
1636     }
1637
1638     CStdString strSQL;
1639     strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
1640     m_pDS->exec(strSQL.c_str());
1641
1642     strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
1643     m_pDS->exec(strSQL.c_str());
1644
1645     strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
1646     m_pDS->exec(strSQL.c_str());
1647
1648     strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
1649     m_pDS->exec(strSQL.c_str());
1650
1651     // remove all info other than the id
1652     // we do this due to the way we have the link between the file + movie tables.
1653
1654     strSQL = "update tvshow set ";
1655     for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1656       strSQL += StringUtils::Format("c%02d=NULL,", iType);
1657
1658     strSQL = strSQL.Mid(0, strSQL.size() - 1) + PrepareSQL(" where idShow=%i", idTvShow);
1659     m_pDS->exec(strSQL.c_str());
1660   }
1661   catch (...)
1662   {
1663     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1664   }
1665 }
1666
1667 //********************************************************************************************************************************
1668 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
1669 {
1670   Filter filter;
1671   filter.join  = "LEFT JOIN actorlinkmovie ON actorlinkmovie.idMovie=movieview.idMovie "
1672                  "LEFT JOIN actors a ON a.idActor=actorlinkmovie.idActor "
1673                  "LEFT JOIN directorlinkmovie ON directorlinkmovie.idMovie=movieview.idMovie "
1674                  "LEFT JOIN actors d ON d.idActor=directorlinkmovie.idDirector";
1675   filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1676   filter.group = "movieview.idMovie";
1677   GetMoviesByWhere("videodb://movies/titles/", filter, items);
1678 }
1679
1680 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
1681 {
1682   Filter filter;
1683   filter.join  = "LEFT JOIN actorlinktvshow ON actorlinktvshow.idShow=tvshowview.idShow "
1684                  "LEFT JOIN actors a ON a.idActor=actorlinktvshow.idActor "
1685                  "LEFT JOIN directorlinktvshow ON directorlinktvshow.idShow=tvshowview.idShow "
1686                  "LEFT JOIN actors d ON d.idActor=directorlinktvshow.idDirector";
1687   filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1688   filter.group = "tvshowview.idShow";
1689   GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
1690 }
1691
1692 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
1693 {
1694   Filter filter;
1695   filter.join  = "LEFT JOIN actorlinkepisode ON actorlinkepisode.idEpisode=episodeview.idEpisode "
1696                  "LEFT JOIN actors a ON a.idActor=actorlinkepisode.idActor "
1697                  "LEFT JOIN directorlinkepisode ON directorlinkepisode.idEpisode=episodeview.idEpisode "
1698                  "LEFT JOIN actors d ON d.idActor=directorlinkepisode.idDirector";
1699   filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1700   filter.group = "episodeview.idEpisode";
1701   GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
1702 }
1703
1704 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1705 {
1706   try
1707   {
1708     items.Clear();
1709     if (NULL == m_pDB.get()) return ;
1710     if (NULL == m_pDS.get()) return ;
1711
1712     CStdString strSQL;
1713     if (strArtist.empty())  // TODO: SMARTPLAYLISTS what is this here for???
1714       strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1715     else
1716       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());
1717     m_pDS->query( strSQL.c_str() );
1718
1719     while (!m_pDS->eof())
1720     {
1721       CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1722       CFileItemPtr pItem(new CFileItem(tag));
1723       pItem->SetLabel(StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator));
1724       items.Add(pItem);
1725       m_pDS->next();
1726     }
1727     m_pDS->close();
1728   }
1729   catch (...)
1730   {
1731     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1732   }
1733 }
1734
1735 //********************************************************************************************************************************
1736 bool CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
1737 {
1738   try
1739   {
1740     // TODO: Optimize this - no need for all the queries!
1741     if (idMovie < 0)
1742       idMovie = GetMovieId(strFilenameAndPath);
1743     if (idMovie < 0) return false;
1744
1745     CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
1746     if (!m_pDS->query(sql.c_str()))
1747       return false;
1748     details = GetDetailsForMovie(m_pDS, true);
1749     return !details.IsEmpty();
1750   }
1751   catch (...)
1752   {
1753     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1754   }
1755   return false;
1756 }
1757
1758 //********************************************************************************************************************************
1759 bool CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
1760 {
1761   try
1762   {
1763     if (idTvShow < 0)
1764       idTvShow = GetTvShowId(strPath);
1765     if (idTvShow < 0) return false;
1766
1767     CStdString sql = PrepareSQL("SELECT * FROM tvshowview WHERE idShow=%i", idTvShow);
1768     if (!m_pDS->query(sql.c_str()))
1769       return false;
1770     details = GetDetailsForTvShow(m_pDS, true);
1771     return !details.IsEmpty();
1772   }
1773   catch (...)
1774   {
1775     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1776   }
1777   return false;
1778 }
1779
1780 bool CVideoDatabase::GetSeasonInfo(int idSeason, CVideoInfoTag& details)
1781 {
1782   if (idSeason < 0)
1783     return false;
1784
1785   try
1786   {
1787     if (!m_pDB.get() || !m_pDS.get())
1788       return false;
1789
1790     CStdString sql = PrepareSQL("SELECT idShow FROM seasons WHERE idSeason=%i", idSeason);
1791     if (!m_pDS->query(sql.c_str()))
1792       return false;
1793
1794     int idShow = -1;
1795     if (m_pDS->num_rows() == 1)
1796       idShow = m_pDS->fv(0).get_asInt();
1797
1798     m_pDS->close();
1799
1800     if (idShow < 0)
1801       return false;
1802
1803     CFileItemList seasons;
1804     if (!GetSeasonsNav(StringUtils::Format("videodb://tvshows/titles/%ld/", idShow), seasons, -1, -1, -1, -1, idShow, false) || seasons.Size() <= 0)
1805       return false;
1806
1807     for (int index = 0; index < seasons.Size(); index++)
1808     {
1809       const CFileItemPtr season = seasons.Get(index);
1810       if (season->HasVideoInfoTag() && season->GetVideoInfoTag()->m_iDbId == idSeason && season->GetVideoInfoTag()->m_iIdShow == idShow)
1811       {
1812         details = *season->GetVideoInfoTag();
1813         return true;
1814       }
1815     }
1816   }
1817   catch (...)
1818   {
1819     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
1820   }
1821   return false;
1822 }
1823
1824 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
1825 {
1826   try
1827   {
1828     // TODO: Optimize this - no need for all the queries!
1829     if (idEpisode < 0)
1830       idEpisode = GetEpisodeId(strFilenameAndPath);
1831     if (idEpisode < 0) return false;
1832
1833     CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
1834     if (!m_pDS->query(sql.c_str()))
1835       return false;
1836     details = GetDetailsForEpisode(m_pDS, true);
1837     return !details.IsEmpty();
1838   }
1839   catch (...)
1840   {
1841     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1842   }
1843   return false;
1844 }
1845
1846 bool CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
1847 {
1848   try
1849   {
1850     // TODO: Optimize this - no need for all the queries!
1851     if (idMVideo < 0)
1852       idMVideo = GetMusicVideoId(strFilenameAndPath);
1853     if (idMVideo < 0) return false;
1854
1855     CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
1856     if (!m_pDS->query(sql.c_str()))
1857       return false;
1858     details = GetDetailsForMusicVideo(m_pDS, true);
1859     return !details.IsEmpty();
1860   }
1861   catch (...)
1862   {
1863     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1864   }
1865   return false;
1866 }
1867
1868 bool CVideoDatabase::GetSetInfo(int idSet, CVideoInfoTag& details)
1869 {
1870   try
1871   {
1872     if (idSet < 0)
1873       return false;
1874
1875     Filter filter;
1876     filter.where = PrepareSQL("sets.idSet=%d", idSet);
1877     CFileItemList items;
1878     if (!GetSetsByWhere("videodb://movies/sets/", filter, items) ||
1879         items.Size() != 1 ||
1880         !items[0]->HasVideoInfoTag())
1881       return false;
1882
1883     details = *(items[0]->GetVideoInfoTag());
1884     return !details.IsEmpty();
1885   }
1886   catch (...)
1887   {
1888     CLog::Log(LOGERROR, "%s (%d) failed", __FUNCTION__, idSet);
1889   }
1890   return false;
1891 }
1892
1893 bool CVideoDatabase::GetFileInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idFile /* = -1 */)
1894 {
1895   try
1896   {
1897     if (idFile < 0)
1898       idFile = GetFileId(strFilenameAndPath);
1899     if (idFile < 0)
1900       return false;
1901
1902     CStdString sql = PrepareSQL("SELECT * FROM files "
1903                                 "JOIN path ON path.idPath = files.idPath "
1904                                 "LEFT JOIN bookmark ON bookmark.idFile = files.idFile AND bookmark.type = %i "
1905                                 "WHERE files.idFile = %i", CBookmark::RESUME, idFile);
1906     if (!m_pDS->query(sql.c_str()))
1907       return false;
1908
1909     details.m_iFileId = m_pDS->fv("files.idFile").get_asInt();
1910     details.m_strPath = m_pDS->fv("path.strPath").get_asString();
1911     CStdString strFileName = m_pDS->fv("files.strFilename").get_asString();
1912     ConstructPath(details.m_strFileNameAndPath, details.m_strPath, strFileName);
1913     details.m_playCount = max(details.m_playCount, m_pDS->fv("files.playCount").get_asInt());
1914     if (!details.m_lastPlayed.IsValid())
1915       details.m_lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
1916     if (!details.m_dateAdded.IsValid())
1917       details.m_dateAdded.SetFromDBDateTime(m_pDS->fv("files.dateAdded").get_asString());
1918     if (!details.m_resumePoint.IsSet())
1919     {
1920       details.m_resumePoint.timeInSeconds = m_pDS->fv("bookmark.timeInSeconds").get_asInt();
1921       details.m_resumePoint.totalTimeInSeconds = m_pDS->fv("bookmark.totalTimeInSeconds").get_asInt();
1922       details.m_resumePoint.type = CBookmark::RESUME;
1923     }
1924
1925     return !details.IsEmpty();
1926   }
1927   catch (...)
1928   {
1929     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1930   }
1931   return false;
1932 }
1933
1934 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1935 {
1936   // add all directors
1937   for (unsigned int i = 0; i < details.m_director.size(); i++)
1938     vecDirectors.push_back(AddActor(details.m_director[i],""));
1939
1940   // add all genres
1941   for (unsigned int i = 0; i < details.m_genre.size(); i++)
1942     vecGenres.push_back(AddGenre(details.m_genre[i]));
1943   // add all studios
1944   for (unsigned int i = 0; i < details.m_studio.size(); i++)
1945     vecStudios.push_back(AddStudio(details.m_studio[i]));
1946 }
1947
1948 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1949 {
1950   CStdString sql;
1951   for (int i = min + 1; i < max; ++i)
1952   {
1953     switch (offsets[i].type)
1954     {
1955     case VIDEODB_TYPE_STRING:
1956       sql += PrepareSQL("c%02d='%s',", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str());
1957       break;
1958     case VIDEODB_TYPE_INT:
1959       sql += PrepareSQL("c%02d='%i',", i, *(int*)(((char*)&details)+offsets[i].offset));
1960       break;
1961     case VIDEODB_TYPE_COUNT:
1962       {
1963         int value = *(int*)(((char*)&details)+offsets[i].offset);
1964         if (value)
1965           sql += PrepareSQL("c%02d=%i,", i, value);
1966         else
1967           sql += PrepareSQL("c%02d=NULL,", i);
1968       }
1969       break;
1970     case VIDEODB_TYPE_BOOL:
1971       sql += PrepareSQL("c%02d='%s',", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false");
1972       break;
1973     case VIDEODB_TYPE_FLOAT:
1974       sql += PrepareSQL("c%02d='%f',", i, *(float*)(((char*)&details)+offsets[i].offset));
1975       break;
1976     case VIDEODB_TYPE_STRINGARRAY:
1977       sql += PrepareSQL("c%02d='%s',", i, StringUtils::Join(*((std::vector<std::string>*)(((char*)&details)+offsets[i].offset)), g_advancedSettings.m_videoItemSeparator).c_str());
1978       break;
1979     case VIDEODB_TYPE_DATE:
1980       sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDate().c_str());
1981       break;
1982     case VIDEODB_TYPE_DATETIME:
1983       sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDateTime().c_str());
1984       break;
1985     }
1986   }
1987   sql.TrimRight(',');
1988   return sql;
1989 }
1990
1991 //********************************************************************************************************************************
1992 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMovie /* = -1 */)
1993 {
1994   try
1995   {
1996     BeginTransaction();
1997
1998     if (idMovie < 0)
1999       idMovie = GetMovieId(strFilenameAndPath);
2000
2001     if (idMovie > -1)
2002       DeleteMovie(strFilenameAndPath, true, idMovie); // true to keep the table entry
2003     else
2004     {
2005       // only add a new movie if we don't already have a valid idMovie
2006       // (DeleteMovie is called with bKeepId == true so the movie won't
2007       // be removed from the movie table)
2008       idMovie = AddMovie(strFilenameAndPath);
2009       if (idMovie < 0)
2010       {
2011         RollbackTransaction();
2012         return idMovie;
2013       }
2014     }
2015
2016     vector<int> vecDirectors;
2017     vector<int> vecGenres;
2018     vector<int> vecStudios;
2019     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2020
2021     for (unsigned int i = 0; i < vecGenres.size(); ++i)
2022       AddGenreToMovie(idMovie, vecGenres[i]);
2023
2024     for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2025       AddDirectorToMovie(idMovie, vecDirectors[i]);
2026
2027     for (unsigned int i = 0; i < vecStudios.size(); ++i)
2028       AddStudioToMovie(idMovie, vecStudios[i]);
2029
2030     // add writers...
2031     for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2032       AddWriterToMovie(idMovie, AddActor(details.m_writingCredits[i],""));
2033
2034     AddCast(idMovie, "movie", "movie", details.m_cast);
2035
2036     // add set...
2037     int idSet = -1;
2038     if (!details.m_strSet.empty())
2039     {
2040       idSet = AddSet(details.m_strSet);
2041       // add art if not available
2042       map<string, string> setArt;
2043       if (!GetArtForItem(idSet, "set", setArt))
2044         SetArtForItem(idSet, "set", artwork);
2045     }
2046
2047     // add tags...
2048     for (unsigned int i = 0; i < details.m_tags.size(); i++)
2049     {
2050       int idTag = AddTag(details.m_tags[i]);
2051       AddTagToItem(idMovie, idTag, "movie");
2052     }
2053
2054     // add countries...
2055     for (unsigned int i = 0; i < details.m_country.size(); i++)
2056       AddCountryToMovie(idMovie, AddCountry(details.m_country[i]));
2057
2058     if (details.HasStreamDetails())
2059       SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2060
2061     SetArtForItem(idMovie, "movie", artwork);
2062
2063     // query DB for any movies matching imdbid and year
2064     CStdString strSQL = PrepareSQL("select files.playCount, files.lastPlayed from movie,files where files.idFile=movie.idFile and movie.c%02d='%s' and movie.c%02d=%i and movie.idMovie!=%i and files.playCount > 0", VIDEODB_ID_IDENT, details.m_strIMDBNumber.c_str(), VIDEODB_ID_YEAR, details.m_iYear, idMovie);
2065     m_pDS->query(strSQL.c_str());
2066
2067     if (!m_pDS->eof())
2068     {
2069       int playCount = m_pDS->fv("files.playCount").get_asInt();
2070
2071       CDateTime lastPlayed;
2072       lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2073
2074       int idFile = GetFileId(strFilenameAndPath);
2075
2076       // update with playCount and lastPlayed
2077       strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2078       m_pDS->exec(strSQL.c_str());
2079     }
2080
2081     m_pDS->close();
2082
2083     // update our movie table (we know it was added already above)
2084     // and insert the new row
2085     CStdString sql = "update movie set " + GetValueString(details, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
2086     if (idSet > 0)
2087       sql += PrepareSQL(", idSet = %i", idSet);
2088     else
2089       sql += ", idSet = NULL";
2090     sql += PrepareSQL(" where idMovie=%i", idMovie);
2091     m_pDS->exec(sql.c_str());
2092     CommitTransaction();
2093
2094     return idMovie;
2095   }
2096   catch (...)
2097   {
2098     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2099   }
2100   RollbackTransaction();
2101   return -1;
2102 }
2103
2104 int CVideoDatabase::SetDetailsForMovieSet(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idSet /* = -1 */)
2105 {
2106   if (details.m_strTitle.empty())
2107     return -1;
2108
2109   try
2110   {
2111     BeginTransaction();
2112     if (idSet < 0)
2113     {
2114       idSet = AddSet(details.m_strTitle);
2115       if (idSet < 0)
2116       {
2117         RollbackTransaction();
2118         return -1;
2119       }
2120     }
2121
2122     SetArtForItem(idSet, "set", artwork);
2123
2124     // and insert the new row
2125     CStdString sql = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", details.m_strTitle.c_str(), idSet);
2126     m_pDS->exec(sql.c_str());
2127     CommitTransaction();
2128
2129     return idSet;
2130   }
2131   catch (...)
2132   {
2133     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
2134   }
2135   RollbackTransaction();
2136   return -1;
2137 }
2138
2139 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, map<string, string> > &seasonArt, int idTvShow /*= -1 */)
2140 {
2141   try
2142   {
2143     if (!m_pDB.get() || !m_pDS.get())
2144     {
2145       CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
2146       return -1;
2147     }
2148
2149     BeginTransaction();
2150
2151     if (idTvShow < 0)
2152       idTvShow = GetTvShowId(strPath);
2153
2154     if (idTvShow > -1)
2155       DeleteDetailsForTvShow(strPath, idTvShow);
2156     else
2157     {
2158       idTvShow = AddTvShow(strPath);
2159       if (idTvShow < 0)
2160       {
2161         RollbackTransaction();
2162         return idTvShow;
2163       }
2164     }
2165
2166     vector<int> vecDirectors;
2167     vector<int> vecGenres;
2168     vector<int> vecStudios;
2169     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2170
2171     AddCast(idTvShow, "tvshow", "show", details.m_cast);
2172
2173     unsigned int i;
2174     for (i = 0; i < vecGenres.size(); ++i)
2175       AddGenreToTvShow(idTvShow, vecGenres[i]);
2176
2177     for (i = 0; i < vecDirectors.size(); ++i)
2178       AddDirectorToTvShow(idTvShow, vecDirectors[i]);
2179
2180     for (i = 0; i < vecStudios.size(); ++i)
2181       AddStudioToTvShow(idTvShow, vecStudios[i]);
2182
2183     // add tags...
2184     for (unsigned int i = 0; i < details.m_tags.size(); i++)
2185     {
2186       int idTag = AddTag(details.m_tags[i]);
2187       AddTagToItem(idTvShow, idTag, "tvshow");
2188     }
2189
2190     // add "all seasons" - the rest are added in SetDetailsForEpisode
2191     AddSeason(idTvShow, -1);
2192
2193     SetArtForItem(idTvShow, "tvshow", artwork);
2194     for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
2195     {
2196       int idSeason = AddSeason(idTvShow, i->first);
2197       if (idSeason > -1)
2198         SetArtForItem(idSeason, "season", i->second);
2199     }
2200
2201     // and insert the new row
2202     CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
2203     sql += PrepareSQL(" where idShow=%i", idTvShow);
2204     m_pDS->exec(sql.c_str());
2205
2206     CommitTransaction();
2207
2208     return idTvShow;
2209   }
2210   catch (...)
2211   {
2212     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2213   }
2214   RollbackTransaction();
2215   return -1;
2216 }
2217
2218 int CVideoDatabase::SetDetailsForSeason(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idShow, int idSeason /* = -1 */)
2219 {
2220   if (idShow < 0 || details.m_iSeason < 0)
2221     return -1;
2222
2223    try
2224   {
2225     BeginTransaction();
2226     if (idSeason < 0)
2227     {
2228       idSeason = AddSeason(idShow, details.m_iSeason);
2229       if (idSeason < 0)
2230       {
2231         RollbackTransaction();
2232         return -1;
2233       }
2234     }
2235
2236     SetArtForItem(idSeason, "season", artwork);
2237
2238     // and insert the new row
2239     CStdString sql = PrepareSQL("UPDATE seasons SET season=%i WHERE idSeason=%i", details.m_iSeason, idSeason);
2240     m_pDS->exec(sql.c_str());
2241     CommitTransaction();
2242
2243     return idSeason;
2244   }
2245   catch (...)
2246   {
2247     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
2248   }
2249   RollbackTransaction();
2250   return -1;
2251 }
2252
2253 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idShow, int idEpisode)
2254 {
2255   try
2256   {
2257     BeginTransaction();
2258     if (idEpisode < 0)
2259       idEpisode = GetEpisodeId(strFilenameAndPath);
2260
2261     if (idEpisode > 0)
2262       DeleteEpisode(strFilenameAndPath, idEpisode, true); // true to keep the table entry
2263     else
2264     {
2265       // only add a new episode if we don't already have a valid idEpisode
2266       // (DeleteEpisode is called with bKeepId == true so the episode won't
2267       // be removed from the episode table)
2268       idEpisode = AddEpisode(idShow,strFilenameAndPath);
2269       if (idEpisode < 0)
2270       {
2271         RollbackTransaction();
2272         return -1;
2273       }
2274     }
2275
2276     vector<int> vecDirectors;
2277     vector<int> vecGenres;
2278     vector<int> vecStudios;
2279     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2280
2281     AddCast(idEpisode, "episode", "episode", details.m_cast);
2282
2283     // add writers...
2284     for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2285       AddWriterToEpisode(idEpisode, AddActor(details.m_writingCredits[i],""));
2286
2287     for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2288     {
2289       AddDirectorToEpisode(idEpisode, vecDirectors[i]);
2290     }
2291
2292     if (details.HasStreamDetails())
2293     {
2294       if (details.m_iFileId != -1)
2295         SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2296       else
2297         SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
2298     }
2299
2300     // ensure we have this season already added
2301     AddSeason(idShow, details.m_iSeason);
2302
2303     SetArtForItem(idEpisode, "episode", artwork);
2304
2305     // query DB for any episodes matching idShow, Season and Episode
2306     CStdString strSQL = PrepareSQL("select files.playCount, files.lastPlayed from episode, files where files.idFile=episode.idFile and episode.c%02d=%i and episode.c%02d=%i AND episode.idShow=%i and episode.idEpisode!=%i and files.playCount > 0",VIDEODB_ID_EPISODE_SEASON, details.m_iSeason, VIDEODB_ID_EPISODE_EPISODE, details.m_iEpisode, idShow, idEpisode);
2307     m_pDS->query(strSQL.c_str());
2308
2309     if (!m_pDS->eof())
2310     {
2311       int playCount = m_pDS->fv("files.playCount").get_asInt();
2312
2313       CDateTime lastPlayed;
2314       lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2315
2316       int idFile = GetFileId(strFilenameAndPath);
2317
2318       // update with playCount and lastPlayed
2319       strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2320       m_pDS->exec(strSQL.c_str());
2321     }
2322
2323     m_pDS->close();
2324
2325     // and insert the new row
2326     CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
2327     sql += PrepareSQL(" where idEpisode=%i", idEpisode);
2328     m_pDS->exec(sql.c_str());
2329     CommitTransaction();
2330
2331     return idEpisode;
2332   }
2333   catch (...)
2334   {
2335     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2336   }
2337   RollbackTransaction();
2338   return -1;
2339 }
2340
2341 int CVideoDatabase::GetSeasonId(int showID, int season)
2342 {
2343   CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
2344   CStdString id = GetSingleValue("seasons", "idSeason", sql);
2345   if (id.empty())
2346     return -1;
2347   return strtol(id.c_str(), NULL, 10);
2348 }
2349
2350 int CVideoDatabase::AddSeason(int showID, int season)
2351 {
2352   int seasonId = GetSeasonId(showID, season);
2353   if (seasonId < 0)
2354   {
2355     if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
2356       seasonId = (int)m_pDS->lastinsertid();
2357   }
2358   return seasonId;
2359 }
2360
2361 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
2362 {
2363   try
2364   {
2365     BeginTransaction();
2366
2367     if (idMVideo < 0)
2368       idMVideo = GetMusicVideoId(strFilenameAndPath);
2369
2370     if (idMVideo > -1)
2371       DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
2372     else
2373     {
2374       // only add a new musicvideo if we don't already have a valid idMVideo
2375       // (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
2376       // be removed from the musicvideo table)
2377       idMVideo = AddMusicVideo(strFilenameAndPath);
2378       if (idMVideo < 0)
2379       {
2380         RollbackTransaction();
2381         return -1;
2382       }
2383     }
2384
2385     vector<int> vecDirectors;
2386     vector<int> vecGenres;
2387     vector<int> vecStudios;
2388     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2389
2390     // add artists...
2391     if (!details.m_artist.empty())
2392     {
2393       for (unsigned int i = 0; i < details.m_artist.size(); i++)
2394       {
2395         CStdString artist = details.m_artist[i];
2396         artist.Trim();
2397         int idArtist = AddActor(artist,"");
2398         AddArtistToMusicVideo(idMVideo, idArtist);
2399       }
2400     }
2401
2402     unsigned int i;
2403     for (i = 0; i < vecGenres.size(); ++i)
2404     {
2405       AddGenreToMusicVideo(idMVideo, vecGenres[i]);
2406     }
2407
2408     for (i = 0; i < vecDirectors.size(); ++i)
2409     {
2410       AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
2411     }
2412
2413     for (i = 0; i < vecStudios.size(); ++i)
2414     {
2415       AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2416     }
2417
2418     // add tags...
2419     for (unsigned int i = 0; i < details.m_tags.size(); i++)
2420     {
2421       int idTag = AddTag(details.m_tags[i]);
2422       AddTagToItem(idMVideo, idTag, "musicvideo");
2423     }
2424
2425     if (details.HasStreamDetails())
2426       SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2427
2428     SetArtForItem(idMVideo, "musicvideo", artwork);
2429
2430     // update our movie table (we know it was added already above)
2431     // and insert the new row
2432     CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
2433     sql += PrepareSQL(" where idMVideo=%i", idMVideo);
2434     m_pDS->exec(sql.c_str());
2435     CommitTransaction();
2436
2437     return idMVideo;
2438   }
2439   catch (...)
2440   {
2441     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2442   }
2443   RollbackTransaction();
2444   return -1;
2445 }
2446
2447 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2448 {
2449   // AddFile checks to make sure the file isn't already in the DB first
2450   int idFile = AddFile(strFileNameAndPath);
2451   if (idFile < 0)
2452     return;
2453   SetStreamDetailsForFileId(details, idFile);
2454 }
2455
2456 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2457 {
2458   if (idFile < 0)
2459     return;
2460
2461   try
2462   {
2463     BeginTransaction();
2464     m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2465
2466     for (int i=1; i<=details.GetVideoStreamCount(); i++)
2467     {
2468       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2469         "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration, strStereoMode) "
2470         "VALUES (%i,%i,'%s',%f,%i,%i,%i,'%s')",
2471         idFile, (int)CStreamDetail::VIDEO,
2472         details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2473         details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i),
2474         details.GetStereoMode(i).c_str()));
2475     }
2476     for (int i=1; i<=details.GetAudioStreamCount(); i++)
2477     {
2478       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2479         "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2480         "VALUES (%i,%i,'%s',%i,'%s')",
2481         idFile, (int)CStreamDetail::AUDIO,
2482         details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2483         details.GetAudioLanguage(i).c_str()));
2484     }
2485     for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2486     {
2487       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2488         "(idFile, iStreamType, strSubtitleLanguage) "
2489         "VALUES (%i,%i,'%s')",
2490         idFile, (int)CStreamDetail::SUBTITLE,
2491         details.GetSubtitleLanguage(i).c_str()));
2492     }
2493
2494     // update the runtime information, if empty
2495     if (details.GetVideoDuration())
2496     {
2497       vector< pair<string, int> > tables;
2498       tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
2499       tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
2500       tables.push_back(make_pair("musicvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
2501       for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
2502       {
2503         CStdString sql = PrepareSQL("update %s set c%02d=%d where idFile=%d and c%02d=''",
2504                                     i->first.c_str(), i->second, details.GetVideoDuration(), idFile, i->second);
2505         m_pDS->exec(sql);
2506       }
2507     }
2508
2509     CommitTransaction();
2510   }
2511   catch (...)
2512   {
2513     RollbackTransaction();
2514     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2515   }
2516 }
2517
2518 //********************************************************************************************************************************
2519 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2520 {
2521   try
2522   {
2523     if (NULL == m_pDB.get()) return ;
2524     if (NULL == m_pDS.get()) return ;
2525
2526     if (idMovie < 0) return ;
2527
2528     CStdString strSQL;
2529     if (iType == VIDEODB_CONTENT_MOVIES)
2530       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 );
2531     if (iType == VIDEODB_CONTENT_EPISODES)
2532       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 );
2533     if (iType == VIDEODB_CONTENT_TVSHOWS)
2534       strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2535     if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2536       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 );
2537
2538     m_pDS->query( strSQL.c_str() );
2539     if (!m_pDS->eof())
2540     {
2541       if (iType != VIDEODB_CONTENT_TVSHOWS)
2542       {
2543         CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2544         ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2545       }
2546       else
2547         filePath = m_pDS->fv("path.strPath").get_asString();
2548     }
2549     m_pDS->close();
2550   }
2551   catch (...)
2552   {
2553     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2554   }
2555 }
2556
2557 //********************************************************************************************************************************
2558 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend, long partNumber)
2559 {
2560   try
2561   {
2562     if (URIUtils::IsStack(strFilenameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(strFilenameAndPath),false).IsDVDImage())
2563     {
2564       CStackDirectory dir;
2565       CFileItemList fileList;
2566       dir.GetDirectory(strFilenameAndPath, fileList);
2567       if (!bAppend)
2568         bookmarks.clear();
2569       for (int i = fileList.Size() - 1; i >= 0; i--) // put the bookmarks of the highest part first in the list
2570         GetBookMarksForFile(fileList[i]->GetPath(), bookmarks, type, true, (i+1));
2571     }
2572     else
2573     {
2574       int idFile = GetFileId(strFilenameAndPath);
2575       if (idFile < 0) return ;
2576       if (!bAppend)
2577         bookmarks.erase(bookmarks.begin(), bookmarks.end());
2578       if (NULL == m_pDB.get()) return ;
2579       if (NULL == m_pDS.get()) return ;
2580
2581       CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2582       m_pDS->query( strSQL.c_str() );
2583       while (!m_pDS->eof())
2584       {
2585         CBookmark bookmark;
2586         bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2587         bookmark.partNumber = partNumber;
2588         bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2589         bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2590         bookmark.playerState = m_pDS->fv("playerState").get_asString();
2591         bookmark.player = m_pDS->fv("player").get_asString();
2592         bookmark.type = type;
2593         if (type == CBookmark::EPISODE)
2594         {
2595           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);
2596           m_pDS2->query(strSQL2.c_str());
2597           bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2598           bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2599           m_pDS2->close();
2600         }
2601         bookmarks.push_back(bookmark);
2602         m_pDS->next();
2603       }
2604       //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2605       m_pDS->close();
2606     }
2607   }
2608   catch (...)
2609   {
2610     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2611   }
2612 }
2613
2614 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2615 {
2616   VECBOOKMARKS bookmarks;
2617   GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2618   if (bookmarks.size() > 0)
2619   {
2620     bookmark = bookmarks[0];
2621     return true;
2622   }
2623   return false;
2624 }
2625
2626 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2627 {
2628   if (!m_pDB.get() || !m_pDS.get())
2629     return;
2630
2631   int fileID = GetFileId(strFilenameAndPath);
2632   if (fileID < -1)
2633     return;
2634
2635   try
2636   {
2637     CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2638     m_pDS->exec(sql.c_str());
2639   }
2640   catch(...)
2641   {
2642     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2643   }
2644 }
2645
2646 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2647 {
2648   try
2649   {
2650     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);
2651     m_pDS->query(strSQL.c_str());
2652     while (!m_pDS->eof())
2653     {
2654       episodes.push_back(GetDetailsForEpisode(m_pDS));
2655       m_pDS->next();
2656     }
2657     m_pDS->close();
2658   }
2659   catch (...)
2660   {
2661     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2662   }
2663 }
2664
2665 //********************************************************************************************************************************
2666 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2667 {
2668   try
2669   {
2670     int idFile = AddFile(strFilenameAndPath);
2671     if (idFile < 0)
2672       return;
2673     if (NULL == m_pDB.get()) return ;
2674     if (NULL == m_pDS.get()) return ;
2675
2676     CStdString strSQL;
2677     int idBookmark=-1;
2678     if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2679     {
2680       strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2681     }
2682     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
2683     {
2684       /* get a bookmark within the same time as previous */
2685       double mintime = bookmark.timeInSeconds - 0.5f;
2686       double maxtime = bookmark.timeInSeconds + 0.5f;
2687       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());
2688     }
2689
2690     if (type != CBookmark::EPISODE)
2691     {
2692       // get current id
2693       m_pDS->query( strSQL.c_str() );
2694       if (m_pDS->num_rows() != 0)
2695         idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2696       m_pDS->close();
2697     }
2698     // update or insert depending if it existed before
2699     if (idBookmark >= 0 )
2700       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);
2701     else
2702       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);
2703
2704     m_pDS->exec(strSQL.c_str());
2705   }
2706   catch (...)
2707   {
2708     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2709   }
2710 }
2711
2712 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2713 {
2714   try
2715   {
2716     int idFile = GetFileId(strFilenameAndPath);
2717     if (idFile < 0) return ;
2718     if (NULL == m_pDB.get()) return ;
2719     if (NULL == m_pDS.get()) return ;
2720
2721     /* a litle bit uggly, we clear first bookmark that is within one second of given */
2722     /* should be no problem since we never add bookmarks that are closer than that   */
2723     double mintime = bookmark.timeInSeconds - 0.5f;
2724     double maxtime = bookmark.timeInSeconds + 0.5f;
2725     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);
2726
2727     m_pDS->query( strSQL.c_str() );
2728     if (m_pDS->num_rows() != 0)
2729     {
2730       int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2731       strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2732       m_pDS->exec(strSQL.c_str());
2733       if (type == CBookmark::EPISODE)
2734       {
2735         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);
2736         m_pDS->exec(strSQL.c_str());
2737       }
2738     }
2739
2740     m_pDS->close();
2741   }
2742   catch (...)
2743   {
2744     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2745   }
2746 }
2747
2748 //********************************************************************************************************************************
2749 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2750 {
2751   try
2752   {
2753     int idFile = GetFileId(strFilenameAndPath);
2754     if (idFile < 0) return ;
2755     if (NULL == m_pDB.get()) return ;
2756     if (NULL == m_pDS.get()) return ;
2757
2758     CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2759     m_pDS->exec(strSQL.c_str());
2760     if (type == CBookmark::EPISODE)
2761     {
2762       strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2763       m_pDS->exec(strSQL.c_str());
2764     }
2765   }
2766   catch (...)
2767   {
2768     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2769   }
2770 }
2771
2772
2773 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2774 {
2775   try
2776   {
2777     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);
2778     m_pDS->query( strSQL.c_str() );
2779     if (!m_pDS->eof())
2780     {
2781       bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2782       bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2783       bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2784       bookmark.playerState = m_pDS->fv("playerState").get_asString();
2785       bookmark.player = m_pDS->fv("player").get_asString();
2786       bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2787     }
2788     else
2789     {
2790       m_pDS->close();
2791       return false;
2792     }
2793     m_pDS->close();
2794   }
2795   catch (...)
2796   {
2797     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2798     return false;
2799   }
2800   return true;
2801 }
2802
2803 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2804 {
2805   try
2806   {
2807     int idFile = GetFileId(tag.m_strFileNameAndPath);
2808     // delete the current episode for the selected episode number
2809     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);
2810     m_pDS->exec(strSQL.c_str());
2811
2812     AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2813     int idBookmark = (int)m_pDS->lastinsertid();
2814     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);
2815     m_pDS->exec(strSQL.c_str());
2816   }
2817   catch (...)
2818   {
2819     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2820   }
2821 }
2822
2823 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2824 {
2825   try
2826   {
2827     CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2828     m_pDS->exec(strSQL.c_str());
2829     strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2830     m_pDS->exec(strSQL.c_str());
2831   }
2832   catch (...)
2833   {
2834     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2835   }
2836 }
2837
2838 //********************************************************************************************************************************
2839 void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
2840 {
2841   if (idMovie < 0)
2842     return;
2843
2844   CStdString path;
2845   GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
2846   if (!path.empty())
2847     DeleteMovie(path, bKeepId, idMovie);
2848 }
2849
2850 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
2851 {
2852   try
2853   {
2854     if (NULL == m_pDB.get()) return ;
2855     if (NULL == m_pDS.get()) return ;
2856     if (idMovie < 0)
2857     {
2858       idMovie = GetMovieId(strFilenameAndPath);
2859       if (idMovie < 0)
2860         return;
2861     }
2862
2863     BeginTransaction();
2864
2865     CStdString strSQL;
2866     strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2867     m_pDS->exec(strSQL.c_str());
2868
2869     strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2870     m_pDS->exec(strSQL.c_str());
2871
2872     strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2873     m_pDS->exec(strSQL.c_str());
2874
2875     strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2876     m_pDS->exec(strSQL.c_str());
2877
2878     strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2879     m_pDS->exec(strSQL.c_str());
2880
2881     DeleteStreamDetails(GetFileId(strFilenameAndPath));
2882
2883     // keep the movie table entry, linking to tv shows, and bookmarks
2884     // so we can update the data in place
2885     // the ancilliary tables are still purged
2886     if (!bKeepId)
2887     {
2888       ClearBookMarksOfFile(strFilenameAndPath);
2889
2890       strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2891       m_pDS->exec(strSQL.c_str());
2892
2893       strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2894       m_pDS->exec(strSQL.c_str());
2895     }
2896
2897     //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2898     if (!bKeepId)
2899       AnnounceRemove("movie", idMovie);
2900
2901     CStdString strPath, strFileName;
2902     SplitPath(strFilenameAndPath,strPath,strFileName);
2903     InvalidatePathHash(strPath);
2904     CommitTransaction();
2905
2906   }
2907   catch (...)
2908   {
2909     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2910     RollbackTransaction();
2911   }
2912 }
2913
2914 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2915 {
2916   if (idTvShow < 0)
2917     return;
2918
2919   CStdString path;
2920   GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2921   if (!path.empty())
2922     DeleteTvShow(path, bKeepId, idTvShow);
2923 }
2924
2925 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2926 {
2927   try
2928   {
2929     if (NULL == m_pDB.get()) return ;
2930     if (NULL == m_pDS.get()) return ;
2931     if (idTvShow < 0)
2932     {
2933       idTvShow = GetTvShowId(strPath);
2934       if (idTvShow < 0)
2935         return;
2936     }
2937
2938     BeginTransaction();
2939
2940     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);
2941     m_pDS2->query(strSQL.c_str());
2942     while (!m_pDS2->eof())
2943     {
2944       CStdString strFilenameAndPath;
2945       CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2946       CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2947       ConstructPath(strFilenameAndPath, strPath, strFileName);
2948       DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2949       m_pDS2->next();
2950     }
2951
2952     DeleteDetailsForTvShow(strPath, idTvShow);
2953
2954     strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2955     m_pDS->exec(strSQL.c_str());
2956
2957     // keep tvshow table and movielink table so we can update data in place
2958     if (!bKeepId)
2959     {
2960       strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2961       m_pDS->exec(strSQL.c_str());
2962
2963       strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2964       m_pDS->exec(strSQL.c_str());
2965
2966       strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2967       m_pDS->exec(strSQL.c_str());
2968     }
2969
2970     //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2971     if (!bKeepId)
2972       AnnounceRemove("tvshow", idTvShow);
2973
2974     InvalidatePathHash(strPath);
2975
2976     CommitTransaction();
2977
2978   }
2979   catch (...)
2980   {
2981     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2982     RollbackTransaction();
2983   }
2984 }
2985
2986 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2987 {
2988   if (idEpisode < 0)
2989     return;
2990
2991   CStdString path;
2992   GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2993   if (!path.empty())
2994     DeleteEpisode(path, idEpisode, bKeepId);
2995 }
2996
2997 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
2998 {
2999   try
3000   {
3001     if (NULL == m_pDB.get()) return ;
3002     if (NULL == m_pDS.get()) return ;
3003     if (idEpisode < 0)
3004     {
3005       idEpisode = GetEpisodeId(strFilenameAndPath);
3006       if (idEpisode < 0)
3007       {
3008         return ;
3009       }
3010     }
3011
3012     //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3013     if (!bKeepId)
3014       AnnounceRemove("episode", idEpisode);
3015
3016     CStdString strSQL;
3017     strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
3018     m_pDS->exec(strSQL.c_str());
3019
3020     strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
3021     m_pDS->exec(strSQL.c_str());
3022
3023     strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
3024     m_pDS->exec(strSQL.c_str());
3025
3026     DeleteStreamDetails(GetFileId(strFilenameAndPath));
3027
3028     // keep episode table entry and bookmarks so we can update the data in place
3029     // the ancilliary tables are still purged
3030     if (!bKeepId)
3031     {
3032       ClearBookMarksOfFile(strFilenameAndPath);
3033
3034       strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
3035       m_pDS->exec(strSQL.c_str());
3036     }
3037
3038   }
3039   catch (...)
3040   {
3041     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3042   }
3043 }
3044
3045 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
3046 {
3047   if (idMusicVideo < 0)
3048     return;
3049
3050   CStdString path;
3051   GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
3052   if (!path.empty())
3053     DeleteMusicVideo(path, bKeepId, idMusicVideo);
3054 }
3055
3056 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
3057 {
3058   try
3059   {
3060     if (NULL == m_pDB.get()) return ;
3061     if (NULL == m_pDS.get()) return ;
3062     if (idMVideo < 0)
3063     {
3064       idMVideo = GetMusicVideoId(strFilenameAndPath);
3065       if (idMVideo < 0)
3066         return;
3067     }
3068
3069     BeginTransaction();
3070
3071     CStdString strSQL;
3072     strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
3073     m_pDS->exec(strSQL.c_str());
3074
3075     strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
3076     m_pDS->exec(strSQL.c_str());
3077
3078     strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
3079     m_pDS->exec(strSQL.c_str());
3080
3081     strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
3082     m_pDS->exec(strSQL.c_str());
3083
3084     DeleteStreamDetails(GetFileId(strFilenameAndPath));
3085
3086     // keep the music video table entry and bookmarks so we can update data in place
3087     // the ancilliary tables are still purged
3088     if (!bKeepId)
3089     {
3090       ClearBookMarksOfFile(strFilenameAndPath);
3091
3092       strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
3093       m_pDS->exec(strSQL.c_str());
3094     }
3095
3096     //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3097     if (!bKeepId)
3098       AnnounceRemove("musicvideo", idMVideo);
3099
3100     CStdString strPath, strFileName;
3101     SplitPath(strFilenameAndPath,strPath,strFileName);
3102     InvalidatePathHash(strPath);
3103     CommitTransaction();
3104
3105   }
3106   catch (...)
3107   {
3108     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3109     RollbackTransaction();
3110   }
3111 }
3112
3113 void CVideoDatabase::DeleteStreamDetails(int idFile)
3114 {
3115     m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
3116 }
3117
3118 void CVideoDatabase::DeleteSet(int idSet)
3119 {
3120   try
3121   {
3122     if (NULL == m_pDB.get()) return ;
3123     if (NULL == m_pDS.get()) return ;
3124
3125     CStdString strSQL;
3126     strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
3127     m_pDS->exec(strSQL.c_str());
3128     strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3129     m_pDS->exec(strSQL.c_str());
3130   }
3131   catch (...)
3132   {
3133     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3134   }
3135 }
3136
3137 void CVideoDatabase::ClearMovieSet(int idMovie)
3138 {
3139   SetMovieSet(idMovie, -1);
3140 }
3141
3142 void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
3143 {
3144   if (idSet >= 0)
3145     ExecuteQuery(PrepareSQL("update movie set idSet = %i where idMovie = %i", idSet, idMovie));
3146   else
3147     ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
3148 }
3149
3150 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3151 {
3152   try
3153   {
3154     if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3155       return;
3156
3157     std::string type;
3158     if (mediaType == VIDEODB_CONTENT_MOVIES)
3159       type = "movie";
3160     else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3161       type = "tvshow";
3162     else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3163       type = "musicvideo";
3164     else
3165       return;
3166
3167     CStdString strSQL;
3168     strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3169     m_pDS->exec(strSQL.c_str());
3170   }
3171   catch (...)
3172   {
3173     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3174   }
3175 }
3176
3177 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3178 {
3179   GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3180 }
3181
3182 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3183 {
3184   for (int i = min + 1; i < max; i++)
3185   {
3186     switch (offsets[i].type)
3187     {
3188     case VIDEODB_TYPE_STRING:
3189       *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3190       break;
3191     case VIDEODB_TYPE_INT:
3192     case VIDEODB_TYPE_COUNT:
3193       *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3194       break;
3195     case VIDEODB_TYPE_BOOL:
3196       *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3197       break;
3198     case VIDEODB_TYPE_FLOAT:
3199       *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3200       break;
3201     case VIDEODB_TYPE_STRINGARRAY:
3202       *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3203       break;
3204     case VIDEODB_TYPE_DATE:
3205       ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3206       break;
3207     case VIDEODB_TYPE_DATETIME:
3208       ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3209       break;
3210     }
3211   }
3212 }
3213
3214 DWORD movieTime = 0;
3215 DWORD castTime = 0;
3216
3217 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3218 {
3219   CVideoInfoTag details;
3220   details.Reset();
3221
3222   switch (type)
3223   {
3224     case VIDEODB_CONTENT_MOVIES:
3225       GetMovieInfo("", details, id);
3226       break;
3227     case VIDEODB_CONTENT_TVSHOWS:
3228       GetTvShowInfo("", details, id);
3229       break;
3230     case VIDEODB_CONTENT_EPISODES:
3231       GetEpisodeInfo("", details, id);
3232       break;
3233     case VIDEODB_CONTENT_MUSICVIDEOS:
3234       GetMusicVideoInfo("", details, id);
3235       break;
3236     default:
3237       break;
3238   }
3239
3240   return details;
3241 }
3242
3243 bool CVideoDatabase::GetStreamDetails(CFileItem& item)
3244 {
3245   // Note that this function (possibly) creates VideoInfoTags for items that don't have one yet!
3246   int fileId = -1;
3247
3248   if (item.HasVideoInfoTag())
3249     fileId = item.GetVideoInfoTag()->m_iFileId;
3250
3251   if (fileId < 0)
3252     fileId = GetFileId(item);
3253
3254   if (fileId < 0)
3255     return false;
3256
3257   // Have a file id, get stream details if available (creates tag either way)
3258   item.GetVideoInfoTag()->m_iFileId = fileId;
3259   return GetStreamDetails(*item.GetVideoInfoTag());
3260 }
3261
3262 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3263 {
3264   if (tag.m_iFileId < 0)
3265     return false;
3266
3267   bool retVal = false;
3268
3269   CStreamDetails& details = tag.m_streamDetails;
3270   details.Reset();
3271
3272   auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3273   try
3274   {
3275     CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3276     pDS->query(strSQL);
3277
3278     while (!pDS->eof())
3279     {
3280       CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3281       switch (e)
3282       {
3283       case CStreamDetail::VIDEO:
3284         {
3285           CStreamDetailVideo *p = new CStreamDetailVideo();
3286           p->m_strCodec = pDS->fv(2).get_asString();
3287           p->m_fAspect = pDS->fv(3).get_asFloat();
3288           p->m_iWidth = pDS->fv(4).get_asInt();
3289           p->m_iHeight = pDS->fv(5).get_asInt();
3290           p->m_iDuration = pDS->fv(10).get_asInt();
3291           p->m_strStereoMode = pDS->fv(11).get_asString();
3292           details.AddStream(p);
3293           retVal = true;
3294           break;
3295         }
3296       case CStreamDetail::AUDIO:
3297         {
3298           CStreamDetailAudio *p = new CStreamDetailAudio();
3299           p->m_strCodec = pDS->fv(6).get_asString();
3300           if (pDS->fv(7).get_isNull())
3301             p->m_iChannels = -1;
3302           else
3303             p->m_iChannels = pDS->fv(7).get_asInt();
3304           p->m_strLanguage = pDS->fv(8).get_asString();
3305           details.AddStream(p);
3306           retVal = true;
3307           break;
3308         }
3309       case CStreamDetail::SUBTITLE:
3310         {
3311           CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3312           p->m_strLanguage = pDS->fv(9).get_asString();
3313           details.AddStream(p);
3314           retVal = true;
3315           break;
3316         }
3317       }
3318
3319       pDS->next();
3320     }
3321
3322     pDS->close();
3323   }
3324   catch (...)
3325   {
3326     CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3327   }
3328   details.DetermineBestStreams();
3329
3330   if (details.GetVideoDuration() > 0)
3331     tag.m_duration = details.GetVideoDuration();
3332
3333   return retVal;
3334 }
3335  
3336 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3337 {
3338   if (tag.m_iFileId < 0)
3339     return false;
3340
3341   bool match = false;
3342
3343   try
3344   {
3345     if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3346     {
3347       CStackDirectory dir;
3348       CFileItemList fileList;
3349       dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3350       tag.m_resumePoint.Reset();
3351       for (int i = fileList.Size() - 1; i >= 0; i--)
3352       {
3353         CBookmark bookmark;
3354         if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3355         {
3356           tag.m_resumePoint = bookmark;
3357           tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3358           match = true;
3359           break;
3360         }
3361       }
3362     }
3363     else
3364     {
3365       CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3366       m_pDS2->query( strSQL.c_str() );
3367       if (!m_pDS2->eof())
3368       {
3369         tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3370         tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3371         tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3372         tag.m_resumePoint.type = CBookmark::RESUME;
3373         match = true;
3374       }
3375       m_pDS2->close();
3376     }
3377   }
3378   catch (...)
3379   {
3380     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3381   }
3382
3383   return match;
3384 }
3385
3386 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3387 {
3388   return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3389 }
3390
3391 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3392 {
3393   CVideoInfoTag details;
3394
3395   if (record == NULL)
3396     return details;
3397
3398   DWORD time = XbmcThreads::SystemClockMillis();
3399   int idMovie = record->at(0).get_asInt();
3400
3401   GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3402
3403   details.m_iDbId = idMovie;
3404   details.m_type = "movie";
3405   
3406   details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3407   details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3408   details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3409   details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3410   CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3411   ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3412   details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3413   details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3414   details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3415   details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3416   details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3417   details.m_resumePoint.type = CBookmark::RESUME;
3418
3419   movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3420
3421   if (getDetails)
3422   {
3423     GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3424
3425     castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3426     details.m_strPictureURL.Parse();
3427
3428     // get tags
3429     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);
3430     m_pDS2->query(strSQL.c_str());
3431     while (!m_pDS2->eof())
3432     {
3433       details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3434       m_pDS2->next();
3435     }
3436
3437     // create tvshowlink string
3438     vector<int> links;
3439     GetLinksToTvShow(idMovie,links);
3440     for (unsigned int i=0;i<links.size();++i)
3441     {
3442       CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3443                          VIDEODB_ID_TV_TITLE,links[i]);
3444       m_pDS2->query(strSQL.c_str());
3445       if (!m_pDS2->eof())
3446         details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3447     }
3448     m_pDS2->close();
3449
3450     // get streamdetails
3451     GetStreamDetails(details);
3452   }
3453   return details;
3454 }
3455
3456 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3457 {
3458   return GetDetailsForTvShow(pDS->get_sql_record(), getDetails);
3459 }
3460
3461 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3462 {
3463   CVideoInfoTag details;
3464
3465   if (record == NULL)
3466     return details;
3467
3468   DWORD time = XbmcThreads::SystemClockMillis();
3469   int idTvShow = record->at(0).get_asInt();
3470
3471   GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3472   details.m_iDbId = idTvShow;
3473   details.m_type = "tvshow";
3474   details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3475   details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3476   details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3477   details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3478   details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3479   details.m_strShowPath = details.m_strPath;
3480   details.m_strShowTitle = details.m_strTitle;
3481
3482   movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3483
3484   if (getDetails)
3485   {
3486     GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3487
3488     // get tags
3489     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);
3490     m_pDS2->query(strSQL.c_str());
3491     while (!m_pDS2->eof())
3492     {
3493       details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3494       m_pDS2->next();
3495     }
3496
3497     castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3498     details.m_strPictureURL.Parse();
3499   }
3500   return details;
3501 }
3502
3503 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3504 {
3505   return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3506 }
3507
3508 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3509 {
3510   CVideoInfoTag details;
3511
3512   if (record == NULL)
3513     return details;
3514
3515   DWORD time = XbmcThreads::SystemClockMillis();
3516   int idEpisode = record->at(0).get_asInt();
3517
3518   GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3519   details.m_iDbId = idEpisode;
3520   details.m_type = "episode";
3521   details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3522   details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3523   CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3524   ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3525   details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3526   details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3527   details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3528   details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3529   details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3530   details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3531   details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3532   details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3533   details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3534   details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3535
3536   details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3537   details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3538   details.m_resumePoint.type = CBookmark::RESUME;
3539
3540   movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3541
3542   if (getDetails)
3543   {
3544     GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3545     GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3546
3547     castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3548     details.m_strPictureURL.Parse();
3549     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);
3550     m_pDS2->query(strSQL.c_str());
3551     if (!m_pDS2->eof())
3552       details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3553     m_pDS2->close();
3554
3555     // get streamdetails
3556     GetStreamDetails(details);
3557   }
3558   return details;
3559 }
3560
3561 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3562 {
3563   return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3564 }
3565
3566 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3567 {
3568   CVideoInfoTag details;
3569
3570   unsigned int time = XbmcThreads::SystemClockMillis();
3571   int idMVideo = record->at(0).get_asInt();
3572
3573   GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3574   details.m_iDbId = idMVideo;
3575   details.m_type = "musicvideo";
3576   
3577   details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3578   details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3579   CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3580   ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3581   details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3582   details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3583   details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3584   details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3585   details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3586   details.m_resumePoint.type = CBookmark::RESUME;
3587
3588   movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3589
3590   if (getDetails)
3591   {
3592     // get tags
3593     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);
3594     m_pDS2->query(strSQL.c_str());
3595     while (!m_pDS2->eof())
3596     {
3597       details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3598       m_pDS2->next();
3599     }
3600     m_pDS2->close();
3601
3602     details.m_strPictureURL.Parse();
3603
3604     // get streamdetails
3605     GetStreamDetails(details);
3606   }
3607   return details;
3608 }
3609
3610 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3611 {
3612   try
3613   {
3614     if (!m_pDB.get()) return;
3615     if (!m_pDS2.get()) return;
3616
3617     CStdString sql = PrepareSQL("SELECT actors.strActor,"
3618                                 "  actorlink%s.strRole,"
3619                                 "  actorlink%s.iOrder,"
3620                                 "  actors.strThumb,"
3621                                 "  art.url "
3622                                 "FROM actorlink%s"
3623                                 "  JOIN actors ON"
3624                                 "    actorlink%s.idActor=actors.idActor"
3625                                 "  LEFT JOIN art ON"
3626                                 "    art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3627                                 "WHERE actorlink%s.%s=%i "
3628                                 "ORDER BY actorlink%s.iOrder",table.c_str(), table.c_str(), table.c_str(), table.c_str(), table.c_str(), table_id.c_str(), type_id, table.c_str());
3629     m_pDS2->query(sql.c_str());
3630     while (!m_pDS2->eof())
3631     {
3632       SActorInfo info;
3633       info.strName = m_pDS2->fv(0).get_asString();
3634       bool found = false;
3635       for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3636       {
3637         if (i->strName == info.strName)
3638         {
3639           found = true;
3640           break;
3641         }
3642       }
3643       if (!found)
3644       {
3645         info.strRole = m_pDS2->fv(1).get_asString();
3646         info.order = m_pDS2->fv(2).get_asInt();
3647         info.thumbUrl.ParseString(m_pDS2->fv(3).get_asString());
3648         info.thumb = m_pDS2->fv(4).get_asString();
3649         cast.push_back(info);
3650       }
3651       m_pDS2->next();
3652     }
3653     m_pDS2->close();
3654   }
3655   catch (...)
3656   {
3657     CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3658   }
3659 }
3660
3661 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3662 /// \retval Returns true if the settings exist, false otherwise.
3663 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3664 {
3665   try
3666   {
3667     // obtain the FileID (if it exists)
3668 #ifdef NEW_VIDEODB_METHODS
3669     if (NULL == m_pDB.get()) return false;
3670     if (NULL == m_pDS.get()) return false;
3671     CStdString strPath, strFileName;
3672     URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3673     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());
3674 #else
3675     int idFile = GetFileId(strFilenameAndPath);
3676     if (idFile < 0) return false;
3677     if (NULL == m_pDB.get()) return false;
3678     if (NULL == m_pDS.get()) return false;
3679     // ok, now obtain the settings for this file
3680     CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3681 #endif
3682     m_pDS->query( strSQL.c_str() );
3683     if (m_pDS->num_rows() > 0)
3684     { // get the video settings info
3685       settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3686       settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3687       settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3688       settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3689       settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3690       settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3691       settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3692       settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3693       settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3694       settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3695       settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3696       settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3697       settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3698       settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3699       settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3700       settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3701       settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3702       settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3703       settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3704       settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3705       settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3706       settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3707       settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3708       settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3709       settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3710       settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3711       settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3712       settings.m_StereoMode = m_pDS->fv("StereoMode").get_asInt();
3713       settings.m_StereoInvert = m_pDS->fv("StereoInvert").get_asBool();
3714       settings.m_SubtitleCached = false;
3715       m_pDS->close();
3716       return true;
3717     }
3718     m_pDS->close();
3719   }
3720   catch (...)
3721   {
3722     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3723   }
3724   return false;
3725 }
3726
3727 /// \brief Sets the settings for a particular video file
3728 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3729 {
3730   try
3731   {
3732     if (NULL == m_pDB.get()) return ;
3733     if (NULL == m_pDS.get()) return ;
3734     int idFile = AddFile(strFilenameAndPath);
3735     if (idFile < 0)
3736       return;
3737     CStdString strSQL = StringUtils::Format("select * from settings where idFile=%i", idFile);
3738     m_pDS->query( strSQL.c_str() );
3739     if (m_pDS->num_rows() > 0)
3740     {
3741       m_pDS->close();
3742       // update the item
3743       strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3744                        "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3745                        "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3746                        "DeinterlaceMode=%i,",
3747                        setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3748                        setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3749                        setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3750                        setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3751                        setting.m_DeinterlaceMode);
3752       CStdString strSQL2;
3753       strSQL2=PrepareSQL("ResumeTime=%i,Crop=%i,CropLeft=%i,CropRight=%i,CropTop=%i,CropBottom=%i,StereoMode=%i,StereoInvert=%i where idFile=%i\n", setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom, setting.m_StereoMode, setting.m_StereoInvert, idFile);
3754       strSQL += strSQL2;
3755       m_pDS->exec(strSQL.c_str());
3756       return ;
3757     }
3758     else
3759     { // add the items
3760       m_pDS->close();
3761       strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3762                 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3763                 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3764                 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3765                 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode,StereoMode,StereoInvert) "
3766               "VALUES ";
3767       strSQL += PrepareSQL("(%i,%i,%i,%f,%f,%f,%i,%i,%f,%i,%f,%f,%f,%f,%f,%i,%i,%i,%i,%i,%i,%i,%f,%f,%i,%i,%i,%i,%i,%i)",
3768                            idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3769                            setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3770                            setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3771                            setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3772                            setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3773                            setting.m_DeinterlaceMode, setting.m_StereoMode, setting.m_StereoInvert);
3774       m_pDS->exec(strSQL.c_str());
3775     }
3776   }
3777   catch (...)
3778   {
3779     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3780   }
3781 }
3782
3783 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3784 {
3785   for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3786     SetArtForItem(mediaId, mediaType, i->first, i->second);
3787 }
3788
3789 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3790 {
3791   try
3792   {
3793     if (NULL == m_pDB.get()) return;
3794     if (NULL == m_pDS.get()) return;
3795
3796     // don't set <foo>.<bar> art types - these are derivative types from parent items
3797     if (artType.find('.') != string::npos)
3798       return;
3799
3800     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());
3801     m_pDS->query(sql.c_str());
3802     if (!m_pDS->eof())
3803     { // update
3804       int artId = m_pDS->fv(0).get_asInt();
3805       m_pDS->close();
3806       sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3807       m_pDS->exec(sql.c_str());
3808     }
3809     else
3810     { // insert
3811       m_pDS->close();
3812       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());
3813       m_pDS->exec(sql.c_str());
3814     }
3815   }
3816   catch (...)
3817   {
3818     CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3819   }
3820 }
3821
3822 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3823 {
3824   try
3825   {
3826     if (NULL == m_pDB.get()) return false;
3827     if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3828
3829     CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3830     m_pDS2->query(sql.c_str());
3831     while (!m_pDS2->eof())
3832     {
3833       art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3834       m_pDS2->next();
3835     }
3836     m_pDS2->close();
3837     return !art.empty();
3838   }
3839   catch (...)
3840   {
3841     CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3842   }
3843   return false;
3844 }
3845
3846 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3847 {
3848   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());
3849   return GetSingleValue(query, m_pDS2);
3850 }
3851
3852 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::string &artType)
3853 {
3854   return ExecuteQuery(PrepareSQL("DELETE FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str()));
3855 }
3856
3857 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::set<std::string> &artTypes)
3858 {
3859   bool result = true;
3860   for (set<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
3861     result &= RemoveArtForItem(mediaId, mediaType, *i);
3862
3863   return result;
3864 }
3865
3866 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3867 {
3868   try
3869   {
3870     if (NULL == m_pDB.get()) return false;
3871     if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3872
3873     // get all seasons for this show
3874     CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3875     m_pDS2->query(sql.c_str());
3876
3877     vector< pair<int, int> > seasons;
3878     while (!m_pDS2->eof())
3879     {
3880       seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3881       m_pDS2->next();
3882     }
3883     m_pDS2->close();
3884
3885     for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3886     {
3887       map<string, string> art;
3888       GetArtForItem(i->first, "season", art);
3889       seasonArt.insert(make_pair(i->second,art));
3890     }
3891     return true;
3892   }
3893   catch (...)
3894   {
3895     CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3896   }
3897   return false;
3898 }
3899
3900 bool CVideoDatabase::GetArtTypes(const std::string &mediaType, std::vector<std::string> &artTypes)
3901 {
3902   try
3903   {
3904     if (NULL == m_pDB.get()) return false;
3905     if (NULL == m_pDS.get()) return false;
3906
3907     CStdString sql = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str());
3908     int numRows = RunQuery(sql);
3909     if (numRows <= 0)
3910       return numRows == 0;
3911
3912     while (!m_pDS->eof())
3913     {
3914       artTypes.push_back(m_pDS->fv(0).get_asString());
3915       m_pDS->next();
3916     }
3917     m_pDS->close();
3918     return true;
3919   }
3920   catch (...)
3921   {
3922     CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str());
3923   }
3924   return false;
3925 }
3926
3927 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3928 /// \retval Returns true if the stack times exist, false otherwise.
3929 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> &times)
3930 {
3931   try
3932   {
3933     // obtain the FileID (if it exists)
3934     int idFile = GetFileId(filePath);
3935     if (idFile < 0) return false;
3936     if (NULL == m_pDB.get()) return false;
3937     if (NULL == m_pDS.get()) return false;
3938     // ok, now obtain the settings for this file
3939     CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3940     m_pDS->query( strSQL.c_str() );
3941     if (m_pDS->num_rows() > 0)
3942     { // get the video settings info
3943       CStdStringArray timeString;
3944       int timeTotal = 0;
3945       StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3946       times.clear();
3947       for (unsigned int i = 0; i < timeString.size(); i++)
3948       {
3949         times.push_back(atoi(timeString[i].c_str()));
3950         timeTotal += atoi(timeString[i].c_str());
3951       }
3952       m_pDS->close();
3953       return (timeTotal > 0);
3954     }
3955     m_pDS->close();
3956   }
3957   catch (...)
3958   {
3959     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3960   }
3961   return false;
3962 }
3963
3964 /// \brief Sets the stack times for a particular video file
3965 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> &times)
3966 {
3967   try
3968   {
3969     if (NULL == m_pDB.get()) return ;
3970     if (NULL == m_pDS.get()) return ;
3971     int idFile = AddFile(filePath);
3972     if (idFile < 0)
3973       return;
3974
3975     // delete any existing items
3976     m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3977
3978     // add the items
3979     CStdString timeString = StringUtils::Format("%i", times[0]);
3980     for (unsigned int i = 1; i < times.size(); i++)
3981       timeString += StringUtils::Format(",%i", times[i]);
3982
3983     m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3984   }
3985   catch (...)
3986   {
3987     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3988   }
3989 }
3990
3991 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
3992 {
3993   if(URIUtils::IsMultiPath(strPath))
3994   {
3995     vector<CStdString> paths;
3996     CMultiPathDirectory::GetPaths(strPath, paths);
3997
3998     for(unsigned i=0;i<paths.size();i++)
3999       RemoveContentForPath(paths[i], progress);
4000   }
4001
4002   try
4003   {
4004     if (NULL == m_pDB.get()) return ;
4005     if (NULL == m_pDS.get()) return ;
4006
4007     if (progress)
4008     {
4009       progress->SetHeading(700);
4010       progress->SetLine(0, "");
4011       progress->SetLine(1, 313);
4012       progress->SetLine(2, 330);
4013       progress->SetPercentage(0);
4014       progress->StartModal();
4015       progress->ShowProgressBar(true);
4016     }
4017     vector< pair<int,string> > paths;
4018     GetSubPaths(strPath, paths);
4019     int iCurr = 0;
4020     for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
4021     {
4022       bool bMvidsChecked=false;
4023       if (progress)
4024       {
4025         progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
4026         progress->Progress();
4027       }
4028
4029       if (HasTvShowInfo(i->second))
4030         DeleteTvShow(i->second);
4031       else
4032       {
4033         CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
4034         m_pDS2->query(strSQL.c_str());
4035         if (m_pDS2->eof())
4036         {
4037           strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4038           m_pDS2->query(strSQL.c_str());
4039           bMvidsChecked = true;
4040         }
4041         while (!m_pDS2->eof())
4042         {
4043           CStdString strMoviePath;
4044           CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
4045           ConstructPath(strMoviePath, i->second, strFileName);
4046           if (HasMovieInfo(strMoviePath))
4047             DeleteMovie(strMoviePath);
4048           if (HasMusicVideoInfo(strMoviePath))
4049             DeleteMusicVideo(strMoviePath);
4050           m_pDS2->next();
4051           if (m_pDS2->eof() && !bMvidsChecked)
4052           {
4053             strSQL =PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4054             m_pDS2->query(strSQL.c_str());
4055             bMvidsChecked = true;
4056           }
4057         }
4058         m_pDS2->close();
4059         m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
4060       }
4061     }
4062   }
4063   catch (...)
4064   {
4065     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
4066   }
4067   if (progress)
4068     progress->Close();
4069 }
4070
4071 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
4072 {
4073   // if we have a multipath, set scraper for all contained paths too
4074   if(URIUtils::IsMultiPath(filePath))
4075   {
4076     vector<CStdString> paths;
4077     CMultiPathDirectory::GetPaths(filePath, paths);
4078
4079     for(unsigned i=0;i<paths.size();i++)
4080       SetScraperForPath(paths[i],scraper,settings);
4081   }
4082
4083   try
4084   {
4085     if (NULL == m_pDB.get()) return ;
4086     if (NULL == m_pDS.get()) return ;
4087
4088     int idPath = AddPath(filePath);
4089     if (idPath < 0)
4090       return;
4091
4092     // Update
4093     CStdString strSQL;
4094     if (settings.exclude)
4095     { //NB See note in ::GetScraperForPath about strContent=='none'
4096       strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
4097     }
4098     else if(!scraper)
4099     { // catch clearing content, but not excluding
4100       strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
4101     }
4102     else
4103     {
4104       CStdString content = TranslateContent(scraper->Content());
4105       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);
4106     }
4107     m_pDS->exec(strSQL.c_str());
4108   }
4109   catch (...)
4110   {
4111     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4112   }
4113 }
4114
4115 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
4116 {
4117   try
4118   {
4119     if (NULL == m_pDB.get()) return false;
4120     if (NULL == m_pDS.get()) return false;
4121
4122     CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
4123     if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
4124       return false;
4125     bool found = m_pDS->fv(0).get_asInt() > 0;
4126     m_pDS->close();
4127     return found;
4128   }
4129   catch (...)
4130   {
4131     CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
4132   }
4133   return false;
4134 }
4135
4136 class CArtItem
4137 {
4138 public:
4139   CArtItem() { art_id = 0; media_id = 0; };
4140   int art_id;
4141   string art_type;
4142   string art_url;
4143   int media_id;
4144   string media_type;
4145 };
4146
4147 bool CVideoDatabase::UpdateOldVersion(int iVersion)
4148 {
4149   if (iVersion < 43)
4150   {
4151     m_pDS->exec("ALTER TABLE settings ADD VerticalShift float");
4152   }
4153   if (iVersion < 44)
4154   {
4155     // only if MySQL is used and default character set is not utf8
4156     // string data needs to be converted to proper utf8
4157     CStdString charset = m_pDS->getDatabase()->getDefaultCharset();
4158     if (!m_sqlite && !charset.empty() && charset != "utf8")
4159     {
4160       map<CStdString, CStdStringArray> tables;
4161       map<CStdString, CStdStringArray>::iterator itt;
4162       CStdStringArray::iterator itc;
4163
4164       // columns that need to be converted
4165       // content columns
4166       CStdStringArray c_columns;
4167       for (int i = 0; i < 22; i++)
4168         c_columns.push_back(StringUtils::Format("c%02d", i));
4169
4170       tables.insert(pair<CStdString, CStdStringArray> ("episode", c_columns));
4171       tables.insert(pair<CStdString, CStdStringArray> ("movie", c_columns));
4172       tables.insert(pair<CStdString, CStdStringArray> ("musicvideo", c_columns));
4173       tables.insert(pair<CStdString, CStdStringArray> ("tvshow", c_columns));
4174
4175       //common columns
4176       CStdStringArray c1;
4177       c1.push_back("strRole");
4178       tables.insert(pair<CStdString, CStdStringArray> ("actorlinkepisode", c1));
4179       tables.insert(pair<CStdString, CStdStringArray> ("actorlinkmovie", c1));
4180       tables.insert(pair<CStdString, CStdStringArray> ("actorlinktvshow", c1));
4181
4182       //remaining columns
4183       CStdStringArray c2;
4184       c2.push_back("strActor");
4185       tables.insert(pair<CStdString, CStdStringArray> ("actors", c2));
4186
4187       CStdStringArray c3;
4188       c3.push_back("strCountry");
4189       tables.insert(pair<CStdString, CStdStringArray> ("country", c3));
4190
4191       CStdStringArray c4;
4192       c4.push_back("strFilename");
4193       tables.insert(pair<CStdString, CStdStringArray> ("files", c4));
4194
4195       CStdStringArray c5;
4196       c5.push_back("strGenre");
4197       tables.insert(pair<CStdString, CStdStringArray> ("genre", c5));
4198
4199       CStdStringArray c6;
4200       c6.push_back("strSet");
4201       tables.insert(pair<CStdString, CStdStringArray> ("sets", c6));
4202
4203       CStdStringArray c7;
4204       c7.push_back("strStudio");
4205       tables.insert(pair<CStdString, CStdStringArray> ("studio", c7));
4206
4207       CStdStringArray c8;
4208       c8.push_back("strPath");
4209       tables.insert(pair<CStdString, CStdStringArray> ("path", c8));
4210
4211       for (itt = tables.begin(); itt != tables.end(); ++itt)
4212       {
4213         CStdString q;
4214         q = PrepareSQL("UPDATE `%s` SET", itt->first.c_str());
4215         for (itc = itt->second.begin(); itc != itt->second.end(); ++itc)
4216         {
4217           q += PrepareSQL(" `%s` = CONVERT(CAST(CONVERT(`%s` USING %s) AS BINARY) USING utf8)",
4218                           itc->c_str(), itc->c_str(), charset.c_str());
4219           if (*itc != itt->second.back())
4220           {
4221             q += ",";
4222           }
4223         }
4224         m_pDS->exec(q);
4225       }
4226     }
4227   }
4228   if (iVersion < 45)
4229   {
4230     m_pDS->exec("ALTER TABLE movie ADD c22 text");
4231     m_pDS->exec("ALTER TABLE episode ADD c22 text");
4232     m_pDS->exec("ALTER TABLE musicvideo ADD c22 text");
4233     m_pDS->exec("ALTER TABLE tvshow ADD c22 text");
4234     // Now update our tables
4235     UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH);
4236     UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH);
4237     UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH);
4238     UpdateBasePath("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, true);
4239   }
4240   if (iVersion < 46)
4241   { // add indices for dir entry lookups
4242     m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c22(255) )");
4243     m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c13(255) )");
4244     m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c18(255) )");
4245     m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c16(255) )");
4246   }
4247   if (iVersion < 50)
4248   {
4249     m_pDS->exec("ALTER TABLE settings ADD ScalingMethod integer");
4250     m_pDS->exec(PrepareSQL("UPDATE settings set ScalingMethod=%i", CMediaSettings::Get().GetDefaultVideoSettings().m_ScalingMethod));
4251   }
4252   if (iVersion < 51)
4253   {
4254     // Add iOrder fields to actorlink* tables to be able to list
4255     // actors by importance
4256     m_pDS->exec("ALTER TABLE actorlinkmovie ADD iOrder integer");
4257     m_pDS->exec("ALTER TABLE actorlinktvshow ADD iOrder integer");
4258     m_pDS->exec("ALTER TABLE actorlinkepisode ADD iOrder integer");
4259   }
4260   if (iVersion < 52)
4261   { // Add basepath link to path table for faster content retrieval, and indicies
4262     m_pDS->exec("ALTER TABLE movie ADD c23 text");
4263     m_pDS->exec("ALTER TABLE episode ADD c23 text");
4264     m_pDS->exec("ALTER TABLE musicvideo ADD c23 text");
4265     m_pDS->exec("ALTER TABLE tvshow ADD c23 text");
4266     m_pDS->dropIndex("movie", "ixMovieBasePath");
4267     m_pDS->dropIndex("musicvideo", "ixMusicVideoBasePath");
4268     m_pDS->dropIndex("episode", "ixEpisodeBasePath");
4269     m_pDS->dropIndex("tvshow", "ixTVShowBasePath");
4270     m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
4271     m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
4272     m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
4273     m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c17(12) )");
4274     // now update the base path links
4275     UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4276     UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4277     UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4278     UpdateBasePathID("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, VIDEODB_ID_TV_PARENTPATHID);
4279   }
4280   if (iVersion < 54)
4281   { // Change INDEX for bookmark table
4282     m_pDS->dropIndex("bookmark", "ix_bookmark");
4283     m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
4284   }
4285   if (iVersion < 55)
4286   {
4287     m_pDS->exec("ALTER TABLE settings ADD DeinterlaceMode integer");
4288     m_pDS->exec("UPDATE settings SET DeinterlaceMode = 2 WHERE Deinterlace NOT IN (0,1)"); // anything other than none: method auto => mode force
4289     m_pDS->exec("UPDATE settings SET DeinterlaceMode = 1 WHERE Deinterlace = 1"); // method auto => mode auto
4290     m_pDS->exec("UPDATE settings SET DeinterlaceMode = 0, Deinterlace = 1 WHERE Deinterlace = 0"); // method none => mode off, method auto
4291   }
4292
4293   if (iVersion < 59)
4294   { // base paths for video_ts and bdmv files was wrong (and inconsistent depending on where and when they were scanned)
4295     CStdString where = PrepareSQL(" WHERE files.strFileName LIKE 'VIDEO_TS.IFO' or files.strFileName LIKE 'index.BDMV'");
4296     UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH, false, where);
4297     UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, false, where);
4298     UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, false, where);
4299     UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4300     UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4301     UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4302   }
4303   if (iVersion < 61)
4304   {
4305     m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4306     m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4307   }
4308   if (iVersion < 62)
4309   { // add seasons table
4310     m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4311     m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
4312     // insert all seasons for each show
4313     m_pDS->query("SELECT idShow FROM tvshow");
4314     while (!m_pDS->eof())
4315     {
4316       CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4317                                   "  SELECT DISTINCT"
4318                                   "    idShow,c%02d"
4319                                   "  FROM"
4320                                   "    episodeview"
4321                                   "  WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4322       m_pDS2->exec(sql.c_str());
4323       // and the "all seasons node"
4324       sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4325       m_pDS2->exec(sql.c_str());
4326       m_pDS->next();
4327     }
4328   }
4329   if (iVersion < 63)
4330   { // add art table
4331     m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4332     m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
4333     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");
4334     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");
4335     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");
4336     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");
4337     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");
4338     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");
4339     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");
4340
4341     CMediaSettings::Get().SetVideoNeedsUpdate(63);
4342     CSettings::Get().Save();
4343   }
4344   if (iVersion < 64)
4345   { // add idShow to episode table
4346     m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4347     m_pDS->query("SELECT idEpisode FROM episode");
4348     while (!m_pDS->eof())
4349     {
4350       int idEpisode = m_pDS->fv(0).get_asInt();
4351       CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4352       m_pDS2->exec(update.c_str());
4353       m_pDS->next();
4354     }
4355     m_pDS->exec("DROP TABLE tvshowlinkepisode");
4356     m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
4357     m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
4358   }
4359   if (iVersion < 67)
4360   {
4361     m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4362     m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
4363
4364     m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4365     m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
4366     m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
4367     m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
4368   }
4369   if (iVersion < 68)
4370   { // add idSet to movie table
4371     m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4372     m_pDS->query("SELECT idMovie FROM movie");
4373     while (!m_pDS->eof())
4374     {
4375       int idMovie = m_pDS->fv(0).get_asInt();
4376       CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4377       m_pDS2->exec(sql.c_str());
4378       m_pDS->next();
4379     }
4380     m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4381   }
4382   if (iVersion < 70)
4383   { // update old art URLs
4384     m_pDS->query("select art_id,url from art where url like 'image://%%'");
4385     vector< pair<int, string> > art;
4386     while (!m_pDS->eof())
4387     {
4388       art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4389       m_pDS->next();
4390     }
4391     m_pDS->close();
4392     for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4393       m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4394   }
4395   if (iVersion < 71)
4396   { // update URL encoded paths
4397     m_pDS->query("select idFile, strFilename from files");
4398     vector< pair<int, string> > files;
4399     while (!m_pDS->eof())
4400     {
4401       files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4402       m_pDS->next();
4403     }
4404     m_pDS->close();
4405
4406     for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4407     {
4408       std::string filename = i->second;
4409       bool update = URIUtils::UpdateUrlEncoding(filename) &&
4410                     (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4411       m_pDS->close();
4412
4413       if (update)
4414         m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4415     }
4416   }
4417   if (iVersion < 72)
4418   { // Update thumb to poster or banner as applicable
4419     CTextureDatabase db;
4420     if (db.Open())
4421     {
4422       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')");
4423       vector<CArtItem> art;
4424       while (!m_pDS->eof())
4425       {
4426         CTextureDetails details;
4427         if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4428         {
4429           CArtItem item;
4430           item.art_id = m_pDS->fv(0).get_asInt();
4431           item.art_url = m_pDS->fv(1).get_asString();
4432           item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4433           item.media_id = m_pDS->fv(2).get_asInt();
4434           item.media_type = m_pDS->fv(3).get_asString();
4435           if (item.art_type != "thumb")
4436             art.push_back(item);
4437         }
4438         m_pDS->next();
4439       }
4440       m_pDS->close();
4441       for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4442       {
4443         if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4444           m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4445         else
4446           m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4447       }
4448     }
4449   }
4450   if (iVersion < 73)
4451   {
4452     m_pDS->exec("DROP TRIGGER IF EXISTS delete_movie");
4453     m_pDS->exec("DROP TRIGGER IF EXISTS delete_tvshow");
4454     m_pDS->exec("DROP TRIGGER IF EXISTS delete_musicvideo");
4455     m_pDS->exec("DROP TRIGGER IF EXISTS delete_episode");
4456     m_pDS->exec("DROP TRIGGER IF EXISTS delete_season");
4457     m_pDS->exec("DROP TRIGGER IF EXISTS delete_set");
4458     m_pDS->exec("DROP TRIGGER IF EXISTS delete_person");
4459
4460     m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
4461                 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
4462                 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
4463                 "END");
4464     m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
4465                 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
4466                 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
4467                 "END");
4468     m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
4469                 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
4470                 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
4471                 "END");
4472     m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
4473                 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
4474                 "END");
4475     m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
4476                 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
4477                 "END");
4478     m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
4479                 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
4480                 "END");
4481     m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
4482                 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
4483                 "END");
4484     m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
4485                 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
4486                 "END");
4487   }
4488   if (iVersion < 74)
4489   { // update the runtime columns
4490     vector< pair<string, int> > tables;
4491     tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4492     tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4493     tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4494     for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4495     {
4496       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);
4497       m_pDS->query(sql.c_str());
4498       vector< pair<int, int> > videos;
4499       while (!m_pDS->eof())
4500       {
4501         int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4502         if (duration)
4503           videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4504         m_pDS->next();
4505       }
4506       m_pDS->close();
4507       for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4508         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));
4509     }
4510   }
4511   if (iVersion < 75)
4512   { // make indices on path, file non-unique (mysql has a prefix index, and prefixes aren't necessarily unique)
4513     m_pDS->dropIndex("path", "ix_path");
4514     m_pDS->dropIndex("files", "ix_files");
4515     m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
4516     m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
4517   }
4518   if (iVersion < 76)
4519   {
4520     m_pDS->exec("ALTER TABLE settings ADD StereoMode integer");
4521     m_pDS->exec("ALTER TABLE settings ADD StereoInvert bool");
4522   }
4523   if (iVersion < 77)
4524     m_pDS->exec("ALTER TABLE streamdetails ADD strStereoMode text");
4525
4526   // always recreate the view after any table change
4527   CreateViews();
4528   return true;
4529 }
4530
4531 int CVideoDatabase::GetMinVersion() const
4532 {
4533   return 77;
4534 }
4535
4536 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4537 {
4538   SScanSettings settings;
4539   bool foundDirectly = false;
4540   ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4541   if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4542     return false; // episodes
4543   return settings.parent_name_root; // shows, movies, musicvids
4544 }
4545
4546 void CVideoDatabase::UpdateBasePath(const char *table, const char *id, int column, bool shows, const CStdString &where)
4547 {
4548   CStdString query;
4549   if (shows)
4550     query = PrepareSQL("SELECT idShow,path.strPath from tvshowlinkpath join path on tvshowlinkpath.idPath=path.idPath");
4551   else
4552     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);
4553   query += where;
4554
4555   map<CStdString, bool> paths;
4556   m_pDS2->query(query.c_str());
4557   while (!m_pDS2->eof())
4558   {
4559     CStdString path(m_pDS2->fv(1).get_asString());
4560     map<CStdString, bool>::iterator i = paths.find(path);
4561     if (i == paths.end())
4562     {
4563       paths.insert(make_pair(path, LookupByFolders(path, shows)));
4564       i = paths.find(path);
4565     }
4566     CStdString filename;
4567     if (!shows)
4568       ConstructPath(filename, path, m_pDS2->fv(2).get_asString());
4569     else
4570       filename = path;
4571     CFileItem item(filename, shows);
4572     path = item.GetBaseMoviePath(i->second);
4573     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());
4574     m_pDS->exec(sql.c_str());
4575     m_pDS2->next();
4576   }
4577   m_pDS2->close();
4578 }
4579
4580 void CVideoDatabase::UpdateBasePathID(const char *table, const char *id, int column, int idColumn)
4581 {
4582   CStdString query = PrepareSQL("SELECT %s,c%02d from %s", id, column, table);
4583   m_pDS2->query(query.c_str());
4584   while (!m_pDS2->eof())
4585   {
4586     int rowID = m_pDS2->fv(0).get_asInt();
4587     CStdString path(m_pDS2->fv(1).get_asString());
4588     // find the parent path of this item
4589     int pathID = AddPath(URIUtils::GetParentPath(path));
4590     if (pathID >= 0)
4591     {
4592       CStdString sql = PrepareSQL("UPDATE %s SET c%02d=%d WHERE %s=%d", table, idColumn, pathID, id, rowID);
4593       m_pDS->exec(sql.c_str());
4594     }
4595     m_pDS2->next();
4596   }
4597   m_pDS2->close();
4598 }
4599
4600 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4601 {
4602   if(URIUtils::IsMultiPath(strPath))
4603   {
4604     vector<CStdString> paths;
4605     CMultiPathDirectory::GetPaths(strPath, paths);
4606
4607     bool ret = false;
4608     for(unsigned i=0;i<paths.size();i++)
4609       ret |= GetPlayCounts(paths[i], items);
4610
4611     return ret;
4612   }
4613   int pathID;
4614   if (URIUtils::IsPlugin(strPath))
4615   {
4616     CURL url(strPath);
4617     pathID = GetPathId(url.GetWithoutFilename());
4618   }
4619   else
4620     pathID = GetPathId(strPath);
4621   if (pathID < 0)
4622     return false; // path (and thus files) aren't in the database
4623
4624   try
4625   {
4626     // error!
4627     if (NULL == m_pDB.get()) return false;
4628     if (NULL == m_pDS.get()) return false;
4629
4630     // TODO: also test a single query for the above and below
4631     CStdString sql = PrepareSQL(
4632       "SELECT"
4633       "  files.strFilename, files.playCount,"
4634       "  bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4635       "FROM files"
4636       "  LEFT JOIN bookmark ON"
4637       "    files.idFile = bookmark.idFile AND bookmark.type = %i"
4638       "  WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4639
4640     if (RunQuery(sql) <= 0)
4641       return false;
4642
4643     items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4644     while (!m_pDS->eof())
4645     {
4646       CStdString path;
4647       ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4648       CFileItemPtr item = items.Get(path);
4649       if (item)
4650       {
4651         item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4652         if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4653         {
4654           item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4655           item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4656           item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4657         }
4658       }
4659       m_pDS->next();
4660     }
4661     return true;
4662   }
4663   catch (...)
4664   {
4665     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4666   }
4667   return false;
4668 }
4669
4670 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4671 {
4672   int id = GetFileId(item);
4673   if (id < 0)
4674     return 0;  // not in db, so not watched
4675
4676   try
4677   {
4678     // error!
4679     if (NULL == m_pDB.get()) return -1;
4680     if (NULL == m_pDS.get()) return -1;
4681
4682     CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4683     int count = 0;
4684     if (m_pDS->query(strSQL.c_str()))
4685     {
4686       // there should only ever be one row returned
4687       if (m_pDS->num_rows() == 1)
4688         count = m_pDS->fv(0).get_asInt();
4689       m_pDS->close();
4690     }
4691     return count;
4692   }
4693   catch (...)
4694   {
4695     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4696   }
4697   return -1;
4698 }
4699
4700 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4701 {
4702   if (NULL == m_pDB.get()) return;
4703   if (NULL == m_pDS.get()) return;
4704   if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4705
4706   CStdString exec;
4707   if (type == VIDEODB_CONTENT_TVSHOWS)
4708     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);
4709   else if (type == VIDEODB_CONTENT_MOVIES)
4710     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);
4711
4712   try
4713   {
4714     m_pDS->exec(exec.c_str());
4715
4716     if (type == VIDEODB_CONTENT_TVSHOWS)
4717       AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4718     else if (type == VIDEODB_CONTENT_MOVIES)
4719       AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4720   }
4721   catch (...)
4722   {
4723     CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4724   }
4725 }
4726
4727 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4728 {
4729   int id;
4730   if (item.HasProperty("original_listitem_url") &&
4731       URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4732   {
4733     CFileItem item2(item);
4734     item2.SetPath(item.GetProperty("original_listitem_url").asString());
4735     id = AddFile(item2);
4736   }
4737   else
4738     id = AddFile(item);
4739   if (id < 0)
4740     return;
4741
4742   // and mark as watched
4743   try
4744   {
4745     if (NULL == m_pDB.get()) return ;
4746     if (NULL == m_pDS.get()) return ;
4747
4748     CStdString strSQL;
4749     if (count)
4750     {
4751       if (!date.IsValid())
4752         strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4753       else
4754         strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4755     }
4756     else
4757     {
4758       if (!date.IsValid())
4759         strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4760       else
4761         strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4762     }
4763
4764     m_pDS->exec(strSQL.c_str());
4765
4766     // We only need to announce changes to video items in the library
4767     if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4768     {
4769       // Only provide the "playcount" value if it has actually changed
4770       if (item.GetVideoInfoTag()->m_playCount != count)
4771       {
4772         CVariant data;
4773         data["playcount"] = count;
4774         ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4775       }
4776       else
4777         ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4778     }
4779   }
4780   catch (...)
4781   {
4782     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4783   }
4784 }
4785
4786 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4787 {
4788   SetPlayCount(item, GetPlayCount(item) + 1);
4789 }
4790
4791 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4792 {
4793   SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4794 }
4795
4796 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4797 {
4798   try
4799   {
4800     if (NULL == m_pDB.get()) return ;
4801     if (NULL == m_pDS.get()) return ;
4802     CStdString content;
4803     if (iType == VIDEODB_CONTENT_MOVIES)
4804     {
4805       CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4806       content = "movie";
4807     }
4808     else if (iType == VIDEODB_CONTENT_EPISODES)
4809     {
4810       CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4811       content = "episode";
4812     }
4813     else if (iType == VIDEODB_CONTENT_TVSHOWS)
4814     {
4815       CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4816       content = "tvshow";
4817     }
4818     else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4819     {
4820       CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4821       content = "musicvideo";
4822     }
4823     else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4824     {
4825       CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4826       CStdString strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4827       m_pDS->exec(strSQL.c_str());
4828     }
4829
4830     if (!content.empty())
4831     {
4832       SetSingleValue(iType, idMovie, FieldTitle, strNewMovieTitle);
4833       AnnounceUpdate(content, idMovie);
4834     }
4835   }
4836   catch (...)
4837   {
4838     CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4839   }
4840 }
4841
4842 bool CVideoDatabase::UpdateVideoSortTitle(int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType /* = VIDEODB_CONTENT_MOVIES */)
4843 {
4844   try
4845   {
4846     if (NULL == m_pDB.get() || NULL == m_pDS.get())
4847       return false;
4848     if (iType != VIDEODB_CONTENT_MOVIES && iType != VIDEODB_CONTENT_TVSHOWS)
4849       return false;
4850
4851     CStdString content = "movie";
4852     if (iType == VIDEODB_CONTENT_TVSHOWS)
4853       content = "tvshow";
4854
4855     if (SetSingleValue(iType, idDb, FieldSortTitle, strNewSortTitle))
4856     {
4857       AnnounceUpdate(content, idDb);
4858       return true;
4859     }
4860   }
4861   catch (...)
4862   {
4863     CLog::Log(LOGERROR, "%s (int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType) failed on ID: %i and Sort Title: %s", __FUNCTION__, idDb, strNewSortTitle.c_str());
4864   }
4865
4866   return false;
4867 }
4868
4869 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4870 void CVideoDatabase::EraseVideoSettings()
4871 {
4872   try
4873   {
4874     CLog::Log(LOGINFO, "Deleting settings information for all movies");
4875     m_pDS->exec("delete from settings");
4876   }
4877   catch (...)
4878   {
4879     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4880   }
4881 }
4882
4883 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4884 {
4885   return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4886 }
4887
4888 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4889 {
4890   return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4891 }
4892
4893 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4894 {
4895   return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4896 }
4897
4898 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4899 {
4900   try
4901   {
4902     if (NULL == m_pDB.get()) return false;
4903     if (NULL == m_pDS.get()) return false;
4904
4905     CStdString strSQL;
4906     Filter extFilter = filter;
4907     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4908     {
4909       if (idContent == VIDEODB_CONTENT_MOVIES)
4910       {
4911         strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4912         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());
4913         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",
4914                                         type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4915       }
4916       else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4917       {
4918         strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4919         extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4920         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",
4921                                         type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4922       }
4923       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4924       {
4925         strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4926         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());
4927         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",
4928                                         type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4929       }
4930       else
4931         return false;
4932     }
4933     else
4934     {
4935       if (idContent == VIDEODB_CONTENT_MOVIES)
4936       {
4937         strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4938         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());
4939         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",
4940                                         type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4941         extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4942       }
4943       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4944       {
4945         strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4946         extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4947         extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4948                                         type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4949       }
4950       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4951       {
4952         strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4953         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());
4954         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",
4955                                         type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4956         extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4957       }
4958       else
4959         return false;
4960     }
4961
4962     if (countOnly)
4963     {
4964       extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4965       extFilter.group.clear();
4966       extFilter.order.clear();
4967     }
4968     strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4969
4970     CVideoDbUrl videoUrl;
4971     if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4972       return false;
4973
4974     int iRowsFound = RunQuery(strSQL);
4975     if (iRowsFound <= 0)
4976       return iRowsFound == 0;
4977
4978     if (countOnly)
4979     {
4980       CFileItemPtr pItem(new CFileItem());
4981       pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4982       items.Add(pItem);
4983
4984       m_pDS->close();
4985       return true;
4986     }
4987
4988     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4989     {
4990       map<int, pair<CStdString,int> > mapItems;
4991       map<int, pair<CStdString,int> >::iterator it;
4992       while (!m_pDS->eof())
4993       {
4994         int id = m_pDS->fv(0).get_asInt();
4995         CStdString str = m_pDS->fv(1).get_asString();
4996
4997         // was this already found?
4998         it = mapItems.find(id);
4999         if (it == mapItems.end())
5000         {
5001           // check path
5002           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5003           {
5004             if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5005               mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
5006             else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5007               mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
5008           }
5009         }
5010         m_pDS->next();
5011       }
5012       m_pDS->close();
5013
5014       for (it = mapItems.begin(); it != mapItems.end(); ++it)
5015       {
5016         CFileItemPtr pItem(new CFileItem(it->second.first));
5017         pItem->GetVideoInfoTag()->m_iDbId = it->first;
5018         pItem->GetVideoInfoTag()->m_type = type;
5019
5020         CVideoDbUrl itemUrl = videoUrl;
5021         CStdString path = StringUtils::Format("%ld/", it->first);
5022         itemUrl.AppendPath(path);
5023         pItem->SetPath(itemUrl.ToString());
5024
5025         pItem->m_bIsFolder = true;
5026         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5027           pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5028         if (!items.Contains(pItem->GetPath()))
5029         {
5030           pItem->SetLabelPreformated(true);
5031           items.Add(pItem);
5032         }
5033       }
5034     }
5035     else
5036     {
5037       while (!m_pDS->eof())
5038       {
5039         CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5040         pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5041         pItem->GetVideoInfoTag()->m_type = type;
5042
5043         CVideoDbUrl itemUrl = videoUrl;
5044         CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5045         itemUrl.AppendPath(path);
5046         pItem->SetPath(itemUrl.ToString());
5047
5048         pItem->m_bIsFolder = true;
5049         pItem->SetLabelPreformated(true);
5050         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5051         { // fv(3) is the number of videos watched, fv(2) is the total number.  We set the playcount
5052           // only if the number of videos watched is equal to the total number (i.e. every video watched)
5053           pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
5054         }
5055         items.Add(pItem);
5056         m_pDS->next();
5057       }
5058       m_pDS->close();
5059     }
5060     return true;
5061   }
5062   catch (...)
5063   {
5064     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5065   }
5066   return false;
5067 }
5068
5069 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5070 {
5071   CStdString mediaType;
5072   if (idContent == VIDEODB_CONTENT_MOVIES)
5073     mediaType = "movie";
5074   else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5075     mediaType = "tvshow";
5076   else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5077     mediaType = "musicvideo";
5078   else
5079     return false;
5080
5081   try
5082   {
5083     if (NULL == m_pDB.get()) return false;
5084     if (NULL == m_pDS.get()) return false;
5085
5086     CStdString strSQL = "SELECT %s FROM taglinks ";
5087
5088     Filter extFilter = filter;
5089     extFilter.fields = "tag.idTag, tag.strTag";
5090     extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
5091
5092     if (idContent == (int)VIDEODB_CONTENT_MOVIES)
5093       extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
5094
5095     extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
5096     extFilter.AppendGroup("taglinks.idTag");
5097
5098     if (countOnly)
5099     {
5100       extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
5101       extFilter.group.clear();
5102       extFilter.order.clear();
5103     }
5104     strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5105
5106     // parse the base path to get additional filters
5107     CVideoDbUrl videoUrl;
5108     if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5109       return false;
5110
5111     int iRowsFound = RunQuery(strSQL);
5112     if (iRowsFound <= 0)
5113       return iRowsFound == 0;
5114
5115     if (countOnly)
5116     {
5117       CFileItemPtr pItem(new CFileItem());
5118       pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5119       items.Add(pItem);
5120
5121       m_pDS->close();
5122       return true;
5123     }
5124
5125     while (!m_pDS->eof())
5126     {
5127       int idTag = m_pDS->fv(0).get_asInt();
5128
5129       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5130       pItem->m_bIsFolder = true;
5131       pItem->GetVideoInfoTag()->m_iDbId = idTag;
5132       pItem->GetVideoInfoTag()->m_type = "tag";
5133
5134       CVideoDbUrl itemUrl = videoUrl;
5135       CStdString path = StringUtils::Format("%ld/", idTag);
5136       itemUrl.AppendPath(path);
5137       pItem->SetPath(itemUrl.ToString());
5138
5139       if (!items.Contains(pItem->GetPath()))
5140       {
5141         pItem->SetLabelPreformated(true);
5142         items.Add(pItem);
5143       }
5144
5145       m_pDS->next();
5146     }
5147     m_pDS->close();
5148
5149     return true;
5150   }
5151   catch (...)
5152   {
5153     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5154   }
5155   return false;
5156 }
5157
5158 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool ignoreSingleMovieSets /* = false */)
5159 {
5160   if (idContent != VIDEODB_CONTENT_MOVIES)
5161     return false;
5162
5163   return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
5164 }
5165
5166 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
5167 {
5168   try
5169   {
5170     if (NULL == m_pDB.get()) return false;
5171     if (NULL == m_pDS.get()) return false;
5172
5173     CVideoDbUrl videoUrl;
5174     if (!videoUrl.FromString(strBaseDir))
5175       return false;
5176
5177     Filter setFilter = filter;
5178     setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
5179     if (!setFilter.order.empty())
5180       setFilter.order += ",";
5181     setFilter.order += "sets.idSet";
5182
5183     if (!GetMoviesByWhere(strBaseDir, setFilter, items))
5184       return false;
5185
5186     CFileItemList sets;
5187     if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
5188       return false;
5189
5190     items.ClearItems();
5191     items.Append(sets);
5192
5193     return true;
5194   }
5195   catch (...)
5196   {
5197     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5198   }
5199   return false;
5200 }
5201
5202 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5203 {
5204   try
5205   {
5206     if (NULL == m_pDB.get()) return false;
5207     if (NULL == m_pDS.get()) return false;
5208
5209     CStdString strSQL = "select %s from musicvideoview ";
5210     Filter extFilter = filter;
5211     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5212     {
5213       extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor, path.strPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
5214       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");
5215     }
5216     else
5217     {
5218       extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
5219       extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist");
5220     }
5221     if (idArtist > -1)
5222       extFilter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", idArtist));
5223
5224     extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
5225
5226     if (countOnly)
5227     {
5228       extFilter.fields = "COUNT(1)";
5229       extFilter.group.clear();
5230       extFilter.order.clear();
5231     }
5232     strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5233
5234     CVideoDbUrl videoUrl;
5235     if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5236       return false;
5237
5238     int iRowsFound = RunQuery(strSQL);
5239     if (iRowsFound <= 0)
5240       return iRowsFound == 0;
5241
5242     if (countOnly)
5243     {
5244       CFileItemPtr pItem(new CFileItem());
5245       pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5246       items.Add(pItem);
5247
5248       m_pDS->close();
5249       return true;
5250     }
5251
5252     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5253     {
5254       map<int, pair<CStdString,CStdString> > mapAlbums;
5255       map<int, pair<CStdString,CStdString> >::iterator it;
5256       while (!m_pDS->eof())
5257       {
5258         int lidMVideo = m_pDS->fv(1).get_asInt();
5259         CStdString strAlbum = m_pDS->fv(0).get_asString();
5260         it = mapAlbums.find(lidMVideo);
5261         // was this genre already found?
5262         if (it == mapAlbums.end())
5263         {
5264           // check path
5265           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5266             mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5267         }
5268         m_pDS->next();
5269       }
5270       m_pDS->close();
5271
5272       for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5273       {
5274         if (!it->second.first.empty())
5275         {
5276           CFileItemPtr pItem(new CFileItem(it->second.first));
5277
5278           CVideoDbUrl itemUrl = videoUrl;
5279           CStdString path = StringUtils::Format("%ld/", it->first);
5280           itemUrl.AppendPath(path);
5281           pItem->SetPath(itemUrl.ToString());
5282
5283           pItem->m_bIsFolder=true;
5284           pItem->SetLabelPreformated(true);
5285           if (!items.Contains(pItem->GetPath()))
5286           {
5287             pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5288             items.Add(pItem);
5289           }
5290         }
5291       }
5292     }
5293     else
5294     {
5295       while (!m_pDS->eof())
5296       {
5297         if (!m_pDS->fv(0).get_asString().empty())
5298         {
5299           CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5300
5301           CVideoDbUrl itemUrl = videoUrl;
5302           CStdString path = StringUtils::Format("%ld/", m_pDS->fv(1).get_asInt());
5303           itemUrl.AppendPath(path);
5304           pItem->SetPath(itemUrl.ToString());
5305
5306           pItem->m_bIsFolder=true;
5307           pItem->SetLabelPreformated(true);
5308           if (!items.Contains(pItem->GetPath()))
5309           {
5310             pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5311             items.Add(pItem);
5312           }
5313         }
5314         m_pDS->next();
5315       }
5316       m_pDS->close();
5317     }
5318
5319 //    CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5320     return true;
5321   }
5322   catch (...)
5323   {
5324     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5325   }
5326   return false;
5327 }
5328
5329 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5330 {
5331   return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5332 }
5333
5334 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5335 {
5336   return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5337 }
5338
5339 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5340 {
5341   if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5342   { // set thumbs - ideally this should be in the normal thumb setting routines
5343     for (int i = 0; i < items.Size() && !countOnly; i++)
5344     {
5345       CFileItemPtr pItem = items[i];
5346       if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5347         pItem->SetIconImage("DefaultArtist.png");
5348       else
5349         pItem->SetIconImage("DefaultActor.png");
5350     }
5351     return true;
5352   }
5353   return false;
5354 }
5355
5356 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5357 {
5358   if (NULL == m_pDB.get()) return false;
5359   if (NULL == m_pDS.get()) return false;
5360
5361   try
5362   {
5363     // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5364     //       point.  This means that we *MUST* filter these levels as you'll get double ups.  Ideally we'd allow playcount
5365     //       to filter through as we normally do for tvshows to save this happening.
5366     //       Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5367     //       Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5368     //       above titles).
5369
5370     // General routine that the other actor/director/writer routines call
5371
5372     // get primary genres for movies
5373     CStdString strSQL;
5374     Filter extFilter = filter;
5375     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5376     {
5377       if (idContent == VIDEODB_CONTENT_MOVIES)
5378       {
5379         strSQL = "select %s from actors ";
5380         extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5381         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",
5382                                         type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5383       }
5384       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5385       {
5386         strSQL = "select %s from actors ";
5387         extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5388         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",
5389                                         type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5390       }
5391       else if (idContent == VIDEODB_CONTENT_EPISODES)
5392       {
5393         strSQL = "select %s from actors ";
5394         extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5395         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",
5396                                         type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5397       }
5398       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5399       {
5400         strSQL = "select %s from actors ";
5401         extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5402         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",
5403                                         type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5404       }
5405       else
5406         return false;
5407     }
5408     else
5409     {
5410       if (idContent == VIDEODB_CONTENT_MOVIES)
5411       {
5412         strSQL ="select %s from actors ";
5413         extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5414         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",
5415                                         type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5416         extFilter.AppendGroup("actors.idActor");
5417       }
5418       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5419       {
5420         strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5421         extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5422         extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5423                                          type.c_str(), type.c_str(), type.c_str()));
5424       }
5425       else if (idContent == VIDEODB_CONTENT_EPISODES)
5426       {
5427         strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5428         extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5429         extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5430                                          type.c_str(), type.c_str(), type.c_str()));
5431         extFilter.AppendGroup("actors.idActor");
5432       }
5433       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5434       {
5435         strSQL = "select %s from actors ";
5436         extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5437         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",
5438                                         type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5439         extFilter.AppendGroup("actors.idActor");
5440       }
5441       else
5442         return false;
5443     }
5444
5445     if (countOnly)
5446     {
5447       extFilter.fields = "COUNT(1)";
5448       extFilter.group.clear();
5449       extFilter.order.clear();
5450     }
5451     strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5452
5453     CVideoDbUrl videoUrl;
5454     if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5455       return false;
5456
5457     // run query
5458     unsigned int time = XbmcThreads::SystemClockMillis();
5459     if (!m_pDS->query(strSQL.c_str())) return false;
5460     CLog::Log(LOGDEBUG, "%s -  query took %i ms",
5461               __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5462     int iRowsFound = m_pDS->num_rows();
5463     if (iRowsFound == 0)
5464     {
5465       m_pDS->close();
5466       return true;
5467     }
5468
5469     if (countOnly)
5470     {
5471       CFileItemPtr pItem(new CFileItem());
5472       pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5473       items.Add(pItem);
5474
5475       m_pDS->close();
5476       return true;
5477     }
5478
5479     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5480     {
5481       map<int, CActor> mapActors;
5482       map<int, CActor>::iterator it;
5483
5484       while (!m_pDS->eof())
5485       {
5486         int idActor = m_pDS->fv(0).get_asInt();
5487         CActor actor;
5488         actor.name = m_pDS->fv(1).get_asString();
5489         actor.thumb = m_pDS->fv(2).get_asString();
5490         if (idContent != VIDEODB_CONTENT_TVSHOWS)
5491           actor.playcount = m_pDS->fv(3).get_asInt();
5492         it = mapActors.find(idActor);
5493         // is this actor already known?
5494         if (it == mapActors.end())
5495         {
5496           // check path
5497           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5498             mapActors.insert(pair<int, CActor>(idActor, actor));
5499         }
5500         m_pDS->next();
5501       }
5502       m_pDS->close();
5503
5504       for (it=mapActors.begin();it != mapActors.end();++it)
5505       {
5506         CFileItemPtr pItem(new CFileItem(it->second.name));
5507
5508         CVideoDbUrl itemUrl = videoUrl;
5509         CStdString path = StringUtils::Format("%ld/", it->first);
5510         itemUrl.AppendPath(path);
5511         pItem->SetPath(itemUrl.ToString());
5512
5513         pItem->m_bIsFolder=true;
5514         pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5515         pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5516         pItem->GetVideoInfoTag()->m_iDbId = it->first;
5517         pItem->GetVideoInfoTag()->m_type = type;
5518         items.Add(pItem);
5519       }
5520     }
5521     else
5522     {
5523       while (!m_pDS->eof())
5524       {
5525         try
5526         {
5527           CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5528
5529           CVideoDbUrl itemUrl = videoUrl;
5530           CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5531           itemUrl.AppendPath(path);
5532           pItem->SetPath(itemUrl.ToString());
5533
5534           pItem->m_bIsFolder=true;
5535           pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5536           pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5537           pItem->GetVideoInfoTag()->m_type = type;
5538           if (idContent != VIDEODB_CONTENT_TVSHOWS)
5539           {
5540             // fv(4) is the number of videos watched, fv(3) is the total number.  We set the playcount
5541             // only if the number of videos watched is equal to the total number (i.e. every video watched)
5542             pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5543           }
5544           if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5545             pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5546           items.Add(pItem);
5547           m_pDS->next();
5548         }
5549         catch (...)
5550         {
5551           m_pDS->close();
5552           CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5553           return items.Size() > 0;
5554         }
5555       }
5556       m_pDS->close();
5557     }
5558     CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5559               __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5560
5561     return true;
5562   }
5563   catch (...)
5564   {
5565     m_pDS->close();
5566     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5567   }
5568   return false;
5569 }
5570
5571 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5572 {
5573   try
5574   {
5575     if (NULL == m_pDB.get()) return false;
5576     if (NULL == m_pDS.get()) return false;
5577
5578     CStdString strSQL;
5579     Filter extFilter = filter;
5580     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5581     {
5582       if (idContent == VIDEODB_CONTENT_MOVIES)
5583       {
5584         strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5585         extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5586       }
5587       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5588       {
5589         strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5590         extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5591       }
5592       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5593       {
5594         strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5595         extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5596       }
5597       else
5598         return false;
5599     }
5600     else
5601     {
5602       CStdString group;
5603       if (idContent == VIDEODB_CONTENT_MOVIES)
5604       {
5605         strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5606         extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5607         extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5608       }
5609       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5610         strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5611       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5612       {
5613         strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5614         extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5615         extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5616       }
5617       else
5618         return false;
5619     }
5620
5621     CVideoDbUrl videoUrl;
5622     if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5623       return false;
5624
5625     int iRowsFound = RunQuery(strSQL);
5626     if (iRowsFound <= 0)
5627       return iRowsFound == 0;
5628
5629     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5630     {
5631       map<int, pair<CStdString,int> > mapYears;
5632       map<int, pair<CStdString,int> >::iterator it;
5633       while (!m_pDS->eof())
5634       {
5635         int lYear = 0;
5636         if (idContent == VIDEODB_CONTENT_TVSHOWS)
5637         {
5638           CDateTime time;
5639           time.SetFromDateString(m_pDS->fv(0).get_asString());
5640           lYear = time.GetYear();
5641         }
5642         else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5643           lYear = m_pDS->fv(0).get_asInt();
5644         it = mapYears.find(lYear);
5645         if (it == mapYears.end())
5646         {
5647           // check path
5648           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5649           {
5650             CStdString year = StringUtils::Format("%d", lYear);
5651             if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5652               mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5653             else
5654               mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5655           }
5656         }
5657         m_pDS->next();
5658       }
5659       m_pDS->close();
5660
5661       for (it=mapYears.begin();it != mapYears.end();++it)
5662       {
5663         if (it->first == 0)
5664           continue;
5665         CFileItemPtr pItem(new CFileItem(it->second.first));
5666
5667         CVideoDbUrl itemUrl = videoUrl;
5668         CStdString path = StringUtils::Format("%ld/", it->first);
5669         itemUrl.AppendPath(path);
5670         pItem->SetPath(itemUrl.ToString());
5671
5672         pItem->m_bIsFolder=true;
5673         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5674           pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5675         items.Add(pItem);
5676       }
5677     }
5678     else
5679     {
5680       while (!m_pDS->eof())
5681       {
5682         int lYear = 0;
5683         CStdString strLabel;
5684         if (idContent == VIDEODB_CONTENT_TVSHOWS)
5685         {
5686           CDateTime time;
5687           time.SetFromDateString(m_pDS->fv(0).get_asString());
5688           lYear = time.GetYear();
5689           strLabel = StringUtils::Format("%i",lYear);
5690         }
5691         else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5692         {
5693           lYear = m_pDS->fv(0).get_asInt();
5694           strLabel = m_pDS->fv(0).get_asString();
5695         }
5696         if (lYear == 0)
5697         {
5698           m_pDS->next();
5699           continue;
5700         }
5701         CFileItemPtr pItem(new CFileItem(strLabel));
5702
5703         CVideoDbUrl itemUrl = videoUrl;
5704         CStdString path = StringUtils::Format("%ld/", lYear);
5705         itemUrl.AppendPath(path);
5706         pItem->SetPath(itemUrl.ToString());
5707
5708         pItem->m_bIsFolder=true;
5709         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5710         {
5711           // fv(2) is the number of videos watched, fv(1) is the total number.  We set the playcount
5712           // only if the number of videos watched is equal to the total number (i.e. every video watched)
5713           pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5714         }
5715
5716         // take care of dupes ..
5717         if (!items.Contains(pItem->GetPath()))
5718           items.Add(pItem);
5719
5720         m_pDS->next();
5721       }
5722       m_pDS->close();
5723     }
5724
5725     return true;
5726   }
5727   catch (...)
5728   {
5729     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5730   }
5731   return false;
5732 }
5733
5734 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5735 {
5736   try
5737   {
5738     if (NULL == m_pDB.get()) return false;
5739     if (NULL == m_pDS.get()) return false;
5740
5741     // look for duplicate show titles and stack them into a list
5742     if (idShow == -1)
5743       return false;
5744     CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5745     CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5746     if (!m_pDS->query(strSQL.c_str())) return false;
5747     int iRows = m_pDS->num_rows();
5748     if (iRows == 0) return false; // this should never happen!
5749     if (iRows > 1)
5750     { // more than one show, so stack them up
5751       strIn = "IN (";
5752       while (!m_pDS->eof())
5753       {
5754         strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5755         m_pDS->next();
5756       }
5757       strIn[strIn.GetLength() - 1] = ')'; // replace last , with )
5758     }
5759     m_pDS->close();
5760     return true;
5761   }
5762   catch (...)
5763   {
5764     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5765   }
5766   return false;
5767 }
5768
5769 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5770 {
5771   try
5772   {
5773     if (NULL == m_pDB.get()) return false;
5774     if (NULL == m_pDS.get()) return false;
5775
5776     // parse the base path to get additional filters
5777     CVideoDbUrl videoUrl;
5778     if (!videoUrl.FromString(strBaseDir))
5779       return false;
5780
5781     CStdString strIn = PrepareSQL("= %i", idShow);
5782     GetStackedTvShowList(idShow, strIn);
5783
5784     CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5785                                           "path.strPath, "
5786                                           "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5787                                           "seasons.idSeason, "
5788                                           "count(1), count(files.playCount) "
5789                                           "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);
5790     
5791     Filter filter;
5792     filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5793                              "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5794                              "JOIN files ON files.idFile = episodeview.idFile "
5795                              "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5796                              "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5797     filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5798     filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5799
5800     videoUrl.AddOption("tvshowid", idShow);
5801
5802     if (idActor != -1)
5803       videoUrl.AddOption("actorid", idActor);
5804     else if (idDirector != -1)
5805       videoUrl.AddOption("directorid", idDirector);
5806     else if (idGenre != -1)
5807       videoUrl.AddOption("genreid", idGenre);
5808     else if (idYear != -1)
5809       videoUrl.AddOption("year", idYear);
5810
5811     if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5812       return false;
5813
5814     int iRowsFound = RunQuery(strSQL);
5815     if (iRowsFound <= 0)
5816       return iRowsFound == 0;
5817
5818     // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5819     CStdString showTitle = m_pDS->fv(2).get_asString();
5820     CStdString showPlot = m_pDS->fv(3).get_asString();
5821     CStdString showPremiered = m_pDS->fv(4).get_asString();
5822     CStdString showStudio = m_pDS->fv(6).get_asString();
5823     CStdString showMPAARating = m_pDS->fv(7).get_asString();
5824
5825     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5826     {
5827       map<int, CSeason> mapSeasons;
5828       map<int, CSeason>::iterator it;
5829       while (!m_pDS->eof())
5830       {
5831         int iSeason = m_pDS->fv(0).get_asInt();
5832         it = mapSeasons.find(iSeason);
5833         // check path
5834         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5835         {
5836           m_pDS->next();
5837           continue;
5838         }
5839         if (it == mapSeasons.end())
5840         {
5841           CSeason season;
5842           season.path = m_pDS->fv(1).get_asString();
5843           season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5844           season.id = m_pDS->fv(8).get_asInt();
5845           season.numEpisodes = m_pDS->fv(9).get_asInt();
5846           season.numWatched = m_pDS->fv(10).get_asInt();
5847           mapSeasons.insert(make_pair(iSeason, season));
5848         }
5849         m_pDS->next();
5850       }
5851       m_pDS->close();
5852
5853       for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5854       {
5855         int iSeason = it->first;
5856         CStdString strLabel;
5857         if (iSeason == 0)
5858           strLabel = g_localizeStrings.Get(20381);
5859         else
5860           strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5861         CFileItemPtr pItem(new CFileItem(strLabel));
5862
5863         CVideoDbUrl itemUrl = videoUrl;
5864         CStdString strDir = StringUtils::Format("%ld/", it->first);
5865         itemUrl.AppendPath(strDir);
5866         pItem->SetPath(itemUrl.ToString());
5867
5868         pItem->m_bIsFolder=true;
5869         pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5870         pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5871         pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5872         pItem->GetVideoInfoTag()->m_type = "season";
5873         pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5874         pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5875         pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5876         pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5877         pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5878         pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5879         pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5880         pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5881         pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5882         pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5883         pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5884         pItem->SetProperty("watchedepisodes", it->second.numWatched);
5885         pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5886         if (iSeason == 0) pItem->SetProperty("isspecial", true);
5887         pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5888         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5889         items.Add(pItem);
5890       }
5891     }
5892     else
5893     {
5894       while (!m_pDS->eof())
5895       {
5896         int iSeason = m_pDS->fv(0).get_asInt();
5897         CStdString strLabel;
5898         if (iSeason == 0)
5899           strLabel = g_localizeStrings.Get(20381);
5900         else
5901           strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5902         CFileItemPtr pItem(new CFileItem(strLabel));
5903
5904         CVideoDbUrl itemUrl = videoUrl;
5905         CStdString strDir = StringUtils::Format("%ld/", iSeason);
5906         itemUrl.AppendPath(strDir);
5907         pItem->SetPath(itemUrl.ToString());
5908
5909         pItem->m_bIsFolder=true;
5910         pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5911         pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5912         pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5913         pItem->GetVideoInfoTag()->m_type = "season";
5914         pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5915         pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5916         pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5917         pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5918         pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5919         pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5920         pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5921         pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5922         int totalEpisodes = m_pDS->fv(9).get_asInt();
5923         int watchedEpisodes = m_pDS->fv(10).get_asInt();
5924         pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5925         pItem->SetProperty("totalepisodes", totalEpisodes);
5926         pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5927         pItem->SetProperty("watchedepisodes", watchedEpisodes);
5928         pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5929         if (iSeason == 0) pItem->SetProperty("isspecial", true);
5930         pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5931         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5932         items.Add(pItem);
5933         m_pDS->next();
5934       }
5935       m_pDS->close();
5936     }
5937
5938     // now add any linked movies
5939     if (getLinkedMovies)
5940     {
5941       Filter movieFilter;
5942       movieFilter.join  = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5943       movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5944       CFileItemList movieItems;
5945       GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
5946
5947       if (movieItems.Size() > 0)
5948         items.Append(movieItems);
5949     }
5950
5951     return true;
5952   }
5953   catch (...)
5954   {
5955     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5956   }
5957   return false;
5958 }
5959
5960 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5961 {
5962   if (NULL == m_pDB.get() || NULL == m_pDS.get())
5963     return false;
5964
5965   if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5966     return false;
5967
5968   SortDescription sorting = sortDescription;
5969   if (sortDescription.sortBy == SortByFile ||
5970       sortDescription.sortBy == SortByTitle ||
5971       sortDescription.sortBy == SortBySortTitle ||
5972       sortDescription.sortBy == SortByLabel ||
5973       sortDescription.sortBy == SortByDateAdded ||
5974       sortDescription.sortBy == SortByRating ||
5975       sortDescription.sortBy == SortByYear ||
5976       sortDescription.sortBy == SortByLastPlayed ||
5977       sortDescription.sortBy == SortByPlaycount)
5978     sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5979
5980   bool success = false;
5981   switch (mediaType)
5982   {
5983   case MediaTypeMovie:
5984     success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5985     break;
5986       
5987   case MediaTypeTvShow:
5988     success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
5989     break;
5990       
5991   case MediaTypeEpisode:
5992     success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
5993     break;
5994       
5995   case MediaTypeMusicVideo:
5996     success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
5997     break;
5998
5999   default:
6000     return false;
6001   }
6002
6003   items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
6004   return success;
6005 }
6006
6007 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6008 {
6009   CVideoDbUrl videoUrl;
6010   if (!videoUrl.FromString(strBaseDir))
6011     return false;
6012
6013   return GetItems(strBaseDir, videoUrl.GetType(), videoUrl.GetItemType(), items, filter, sortDescription);
6014 }
6015
6016 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, const CStdString &mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6017 {
6018   VIDEODB_CONTENT_TYPE contentType;
6019   if (mediaType.Equals("movies"))
6020     contentType = VIDEODB_CONTENT_MOVIES;
6021   else if (mediaType.Equals("tvshows"))
6022   {
6023     if (itemType.Equals("episodes"))
6024       contentType = VIDEODB_CONTENT_EPISODES;
6025     else
6026       contentType = VIDEODB_CONTENT_TVSHOWS;
6027   }
6028   else if (mediaType.Equals("musicvideos"))
6029     contentType = VIDEODB_CONTENT_MUSICVIDEOS;
6030   else
6031     return false;
6032
6033   return GetItems(strBaseDir, contentType, itemType, items, filter, sortDescription);
6034 }
6035
6036 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, VIDEODB_CONTENT_TYPE mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6037 {
6038   if (itemType.Equals("movies") && (mediaType == VIDEODB_CONTENT_MOVIES || mediaType == VIDEODB_CONTENT_MOVIE_SETS))
6039     return GetMoviesByWhere(strBaseDir, filter, items, sortDescription);
6040   else if (itemType.Equals("tvshows") && mediaType == VIDEODB_CONTENT_TVSHOWS)
6041     return GetTvShowsByWhere(strBaseDir, filter, items, sortDescription);
6042   else if (itemType.Equals("musicvideos") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6043     return GetMusicVideosByWhere(strBaseDir, filter, items, true, sortDescription);
6044   else if (itemType.Equals("episodes") && mediaType == VIDEODB_CONTENT_EPISODES)
6045     return GetEpisodesByWhere(strBaseDir, filter, items, true, sortDescription);
6046   else if (itemType.Equals("seasons") && mediaType == VIDEODB_CONTENT_TVSHOWS)
6047     return GetSeasonsNav(strBaseDir, items);
6048   else if (itemType.Equals("genres"))
6049     return GetGenresNav(strBaseDir, items, mediaType, filter);
6050   else if (itemType.Equals("years"))
6051     return GetYearsNav(strBaseDir, items, mediaType, filter);
6052   else if (itemType.Equals("actors"))
6053     return GetActorsNav(strBaseDir, items, mediaType, filter);
6054   else if (itemType.Equals("directors"))
6055     return GetDirectorsNav(strBaseDir, items, mediaType, filter);
6056   else if (itemType.Equals("writers"))
6057     return GetWritersNav(strBaseDir, items, mediaType, filter);
6058   else if (itemType.Equals("studios"))
6059     return GetStudiosNav(strBaseDir, items, mediaType, filter);
6060   else if (itemType.Equals("sets"))
6061     return GetSetsNav(strBaseDir, items, mediaType, filter);
6062   else if (itemType.Equals("countries"))
6063     return GetCountriesNav(strBaseDir, items, mediaType, filter);
6064   else if (itemType.Equals("tags"))
6065     return GetTagsNav(strBaseDir, items, mediaType, filter);
6066   else if (itemType.Equals("artists") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6067     return GetActorsNav(strBaseDir, items, mediaType, filter);
6068   else if (itemType.Equals("albums") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6069     return GetMusicVideoAlbumsNav(strBaseDir, items, -1, filter);
6070
6071   return false;
6072 }
6073
6074 CStdString CVideoDatabase::GetItemById(const CStdString &itemType, int id)
6075 {
6076   if (itemType.Equals("genres"))
6077     return GetGenreById(id);
6078   else if (itemType.Equals("years"))
6079     return StringUtils::Format("%d", id);
6080   else if (itemType.Equals("actors") || itemType.Equals("directors") || itemType.Equals("artists"))
6081     return GetPersonById(id);
6082   else if (itemType.Equals("studios"))
6083     return GetStudioById(id);
6084   else if (itemType.Equals("sets"))
6085     return GetSetById(id);
6086   else if (itemType.Equals("countries"))
6087     return GetCountryById(id);
6088   else if (itemType.Equals("tags"))
6089     return GetTagById(id);
6090   else if (itemType.Equals("albums"))
6091     return GetMusicVideoAlbumById(id);
6092
6093   return "";
6094 }
6095
6096 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
6097                                   int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
6098                                   int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
6099                                   const SortDescription &sortDescription /* = SortDescription() */)
6100 {
6101   CVideoDbUrl videoUrl;
6102   if (!videoUrl.FromString(strBaseDir))
6103     return false;
6104
6105   if (idGenre > 0)
6106     videoUrl.AddOption("genreid", idGenre);
6107   else if (idCountry > 0)
6108     videoUrl.AddOption("countryid", idCountry);
6109   else if (idStudio > 0)
6110     videoUrl.AddOption("studioid", idStudio);
6111   else if (idDirector > 0)
6112     videoUrl.AddOption("directorid", idDirector);
6113   else if (idYear > 0)
6114     videoUrl.AddOption("year", idYear);
6115   else if (idActor > 0)
6116     videoUrl.AddOption("actorid", idActor);
6117   else if (idSet > 0)
6118     videoUrl.AddOption("setid", idSet);
6119   else if (idTag > 0)
6120     videoUrl.AddOption("tagid", idTag);
6121
6122   Filter filter;
6123   return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
6124 }
6125
6126 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
6127 {
6128   try
6129   {
6130     movieTime = 0;
6131     castTime = 0;
6132
6133     if (NULL == m_pDB.get()) return false;
6134     if (NULL == m_pDS.get()) return false;
6135
6136     // parse the base path to get additional filters
6137     CVideoDbUrl videoUrl;
6138     Filter extFilter = filter;
6139     SortDescription sorting = sortDescription;
6140     if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
6141       return false;
6142
6143     int total = -1;
6144
6145     CStdString strSQL = "select %s from movieview ";
6146     CStdString strSQLExtra;
6147     if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
6148       return false;
6149
6150     // Apply the limiting directly here if there's no special sorting but limiting
6151     if (extFilter.limit.empty() &&
6152         sorting.sortBy == SortByNone &&
6153        (sorting.limitStart > 0 || sorting.limitEnd > 0))
6154     {
6155       total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6156       strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6157     }
6158
6159     strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6160
6161     int iRowsFound = RunQuery(strSQL);
6162     if (iRowsFound <= 0)
6163       return iRowsFound == 0;
6164
6165     // store the total value of items as a property
6166     if (total < iRowsFound)
6167       total = iRowsFound;
6168     items.SetProperty("total", total);
6169     
6170     DatabaseResults results;
6171     results.reserve(iRowsFound);
6172
6173     if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
6174       return false;
6175
6176     // get data from returned rows
6177     items.Reserve(results.size());
6178     const query_data &data = m_pDS->get_result_set().records;
6179     for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6180     {
6181       unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6182       const dbiplus::sql_record* const record = data.at(targetRow);
6183
6184       CVideoInfoTag movie = GetDetailsForMovie(record);
6185       if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6186           g_passwordManager.bMasterUser                                   ||
6187           g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6188       {
6189         CFileItemPtr pItem(new CFileItem(movie));
6190
6191         CVideoDbUrl itemUrl = videoUrl;
6192         CStdString path = StringUtils::Format("%ld", movie.m_iDbId);
6193         itemUrl.AppendPath(path);
6194         pItem->SetPath(itemUrl.ToString());
6195
6196         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
6197         items.Add(pItem);
6198       }
6199     }
6200
6201     // cleanup
6202     m_pDS->close();
6203     return true;
6204   }
6205   catch (...)
6206   {
6207     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6208   }
6209   return false;
6210 }
6211
6212 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
6213                                   int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
6214                                   const SortDescription &sortDescription /* = SortDescription() */)
6215 {
6216   CVideoDbUrl videoUrl;
6217   if (!videoUrl.FromString(strBaseDir))
6218     return false;
6219
6220   if (idGenre != -1)
6221     videoUrl.AddOption("genreid", idGenre);
6222   else if (idStudio != -1)
6223     videoUrl.AddOption("studioid", idStudio);
6224   else if (idDirector != -1)
6225     videoUrl.AddOption("directorid", idDirector);
6226   else if (idYear != -1)
6227     videoUrl.AddOption("year", idYear);
6228   else if (idActor != -1)
6229     videoUrl.AddOption("actorid", idActor);
6230   else if (idTag != -1)
6231     videoUrl.AddOption("tagid", idTag);
6232
6233   Filter filter;
6234   return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
6235 }
6236
6237 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
6238 {
6239   try
6240   {
6241     movieTime = 0;
6242
6243     if (NULL == m_pDB.get()) return false;
6244     if (NULL == m_pDS.get()) return false;
6245
6246     int total = -1;
6247     
6248     CStdString strSQL = "SELECT %s FROM tvshowview ";
6249     CVideoDbUrl videoUrl;
6250     CStdString strSQLExtra;
6251     Filter extFilter = filter;
6252     SortDescription sorting = sortDescription;
6253     if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6254       return false;
6255
6256     // Apply the limiting directly here if there's no special sorting but limiting
6257       if (extFilter.limit.empty() &&
6258         sorting.sortBy == SortByNone &&
6259         (sorting.limitStart > 0 || sorting.limitEnd > 0))
6260     {
6261       total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6262       strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6263     }
6264
6265     strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6266
6267     int iRowsFound = RunQuery(strSQL);
6268     if (iRowsFound <= 0)
6269       return iRowsFound == 0;
6270
6271     // store the total value of items as a property
6272     if (total < iRowsFound)
6273       total = iRowsFound;
6274     items.SetProperty("total", total);
6275     
6276     DatabaseResults results;
6277     results.reserve(iRowsFound);
6278     if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
6279       return false;
6280
6281     // get data from returned rows
6282     items.Reserve(results.size());
6283     const query_data &data = m_pDS->get_result_set().records;
6284     for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6285     {
6286       unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6287       const dbiplus::sql_record* const record = data.at(targetRow);
6288       
6289       CVideoInfoTag movie = GetDetailsForTvShow(record, false);
6290       if ((CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6291            g_passwordManager.bMasterUser                                     ||
6292            g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video"))) &&
6293           (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
6294       {
6295         CFileItemPtr pItem(new CFileItem(movie));
6296
6297         CVideoDbUrl itemUrl = videoUrl;
6298         CStdString path = StringUtils::Format("%ld/", record->at(0).get_asInt());
6299         itemUrl.AppendPath(path);
6300         pItem->SetPath(itemUrl.ToString());
6301
6302         pItem->m_dateTime = movie.m_premiered;
6303         pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6304         pItem->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
6305         pItem->SetProperty("totalepisodes", movie.m_iEpisode);
6306         pItem->SetProperty("numepisodes", movie.m_iEpisode); // will be changed later to reflect watchmode setting
6307         pItem->SetProperty("watchedepisodes", movie.m_playCount);
6308         pItem->SetProperty("unwatchedepisodes", movie.m_iEpisode - movie.m_playCount);
6309         pItem->GetVideoInfoTag()->m_playCount = (movie.m_iEpisode == movie.m_playCount) ? 1 : 0;
6310         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6311         items.Add(pItem);
6312       }
6313     }
6314
6315     Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6316
6317     // cleanup
6318     m_pDS->close();
6319     return true;
6320   }
6321   catch (...)
6322   {
6323     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6324   }
6325   return false;
6326 }
6327
6328 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6329 {
6330   if (maintainSortOrder)
6331   {
6332     // save current sort order
6333     for (int i = 0; i < items.Size(); i++)
6334       items[i]->m_iprogramCount = i;
6335   }
6336
6337   switch (type)
6338   {
6339     case VIDEODB_CONTENT_TVSHOWS:
6340     {
6341       // sort by Title
6342       items.Sort(SortBySortTitle, SortOrderAscending);
6343
6344       int i = 0;
6345       while (i < items.Size())
6346       {
6347         CFileItemPtr pItem = items.Get(i);
6348         CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6349         CStdString strFanArt = pItem->GetArt("fanart");
6350
6351         int j = i + 1;
6352         bool bStacked = false;
6353         while (j < items.Size())
6354         {
6355           CFileItemPtr jItem = items.Get(j);
6356
6357           // matching title? append information
6358           if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6359           {
6360             if (jItem->GetVideoInfoTag()->m_premiered != 
6361                 pItem->GetVideoInfoTag()->m_premiered)
6362             {
6363               j++;
6364               continue;
6365             }
6366             bStacked = true;
6367
6368             // increment episode counts
6369             pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6370             pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6371             pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6372             pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6373             pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6374
6375             // adjust lastplayed
6376             if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6377               pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6378
6379             // check for fanart if not already set
6380             if (strFanArt.empty())
6381               strFanArt = jItem->GetArt("fanart");
6382
6383             // remove duplicate entry
6384             items.Remove(j);
6385           }
6386           // no match? exit loop
6387           else
6388             break;
6389         }
6390         // update playcount and fanart
6391         if (bStacked)
6392         {
6393           pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6394           pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6395           if (!strFanArt.empty())
6396             pItem->SetArt("fanart", strFanArt);
6397         }
6398         // increment i to j which is the next item
6399         i = j;
6400       }
6401     }
6402     break;
6403     // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6404     // so that if we eventually want to stack during scan we can utilize it.
6405     /*
6406     case VIDEODB_CONTENT_EPISODES:
6407     {
6408       // sort by ShowTitle, Episode, Filename
6409       items.Sort(SortByEpisodeNumber, SortOrderAscending);
6410
6411       int i = 0;
6412       while (i < items.Size())
6413       {
6414         CFileItemPtr pItem = items.Get(i);
6415         CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6416         int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6417         int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6418         //CStdString strFanArt = pItem->GetArt("fanart");
6419
6420         // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6421         CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6422         bool bDvdFolder = StringUtils::EndsWithNoCase(strFileNameAndPath, "video_ts.ifo");
6423
6424         vector<CStdString> paths;
6425         paths.push_back(strFileNameAndPath);
6426         CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6427
6428         int j = i + 1;
6429         int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6430         while (j < items.Size())
6431         {
6432           CFileItemPtr jItem = items.Get(j);
6433           const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6434           CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6435
6436           CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6437           // compare path, season, episode
6438           if (
6439             jTag &&
6440             jTag->m_strPath.Equals(strPath) &&
6441             jTag->m_iSeason == iSeason &&
6442             jTag->m_iEpisode == iEpisode
6443             )
6444           {
6445             // keep checking to see if this is dvd folder
6446             if (!bDvdFolder)
6447             {
6448               bDvdFolder = StringUtils::EndsWithNoCase(jFileNameAndPath, "video_ts.ifo");
6449               // if we have a dvd folder, we stack differently
6450               if (bDvdFolder)
6451               {
6452                 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6453                 paths.empty();
6454                 paths.push_back(jFileNameAndPath);
6455               }
6456               else
6457               {
6458                 // increment playcount
6459                 iPlayCount += jTag->m_playCount;
6460
6461                 // episodes dont have fanart yet
6462                 //if (strFanArt.empty())
6463                 //  strFanArt = jItem->GetArt("fanart");
6464
6465                 paths.push_back(jFileNameAndPath);
6466               }
6467             }
6468
6469             // remove duplicate entry
6470             jTag = NULL;
6471             items.Remove(j);
6472           }
6473           // no match? exit loop
6474           else
6475             break;
6476         }
6477         // update playcount and fanart if we have a stacked entry
6478         if (paths.size() > 1)
6479         {
6480           CStackDirectory dir;
6481           CStdString strStack;
6482           dir.ConstructStackPath(paths, strStack);
6483           pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6484           pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6485           pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6486
6487           // episodes dont have fanart yet
6488           //if (!strFanArt.empty())
6489           //  pItem->SetArt("fanart", strFanArt);
6490         }
6491         // increment i to j which is the next item
6492         i = j;
6493       }
6494     }
6495     break;*/
6496
6497     // stack other types later
6498     default:
6499       break;
6500   }
6501   if (maintainSortOrder)
6502   {
6503     // restore original sort order - essential for smartplaylists
6504     items.Sort(SortByProgramCount, SortOrderAscending);
6505   }
6506 }
6507
6508 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6509 {
6510   CVideoDbUrl videoUrl;
6511   if (!videoUrl.FromString(strBaseDir))
6512     return false;
6513
6514   CStdString strIn;
6515   if (idShow != -1)
6516   {
6517     strIn = PrepareSQL("= %i", idShow);
6518     GetStackedTvShowList(idShow, strIn);
6519
6520     videoUrl.AddOption("tvshowid", idShow);
6521     if (idSeason >= 0)
6522       videoUrl.AddOption("season", idSeason);
6523
6524     if (idGenre != -1)
6525       videoUrl.AddOption("genreid", idGenre);
6526     else if (idYear !=-1)
6527       videoUrl.AddOption("year", idYear);
6528     else if (idActor != -1)
6529       videoUrl.AddOption("actorid", idActor);
6530   }
6531   else if (idYear != -1)
6532     videoUrl.AddOption("year", idYear);
6533   
6534   if (idDirector != -1)
6535     videoUrl.AddOption("directorid", idDirector);
6536
6537   Filter filter;
6538   bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6539
6540   if (idSeason == -1 && idShow != -1)
6541   { // add any linked movies
6542     Filter movieFilter;
6543     movieFilter.join  = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6544     movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6545     CFileItemList movieItems;
6546     GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
6547
6548     if (movieItems.Size() > 0)
6549       items.Append(movieItems);
6550   }
6551
6552   return ret;
6553 }
6554
6555 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6556 {
6557   try
6558   {
6559     movieTime = 0;
6560     castTime = 0;
6561
6562     if (NULL == m_pDB.get()) return false;
6563     if (NULL == m_pDS.get()) return false;
6564
6565     int total = -1;
6566     
6567     CStdString strSQL = "select %s from episodeview ";
6568     CVideoDbUrl videoUrl;
6569     CStdString strSQLExtra;
6570     Filter extFilter = filter;
6571     SortDescription sorting = sortDescription;
6572     if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6573       return false;
6574
6575     // Apply the limiting directly here if there's no special sorting but limiting
6576     if (extFilter.limit.empty() &&
6577       sorting.sortBy == SortByNone &&
6578       (sorting.limitStart > 0 || sorting.limitEnd > 0))
6579     {
6580       total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6581       strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6582     }
6583
6584     strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6585
6586     int iRowsFound = RunQuery(strSQL);
6587     if (iRowsFound <= 0)
6588       return iRowsFound == 0;
6589
6590     // store the total value of items as a property
6591     if (total < iRowsFound)
6592       total = iRowsFound;
6593     items.SetProperty("total", total);
6594     
6595     DatabaseResults results;
6596     results.reserve(iRowsFound);
6597     if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6598       return false;
6599     
6600     // get data from returned rows
6601     items.Reserve(results.size());
6602     CLabelFormatter formatter("%H. %T", "");
6603
6604     const query_data &data = m_pDS->get_result_set().records;
6605     for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6606     {
6607       unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6608       const dbiplus::sql_record* const record = data.at(targetRow);
6609
6610       CVideoInfoTag movie = GetDetailsForEpisode(record);
6611       if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6612           g_passwordManager.bMasterUser                                     ||
6613           g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6614       {
6615         CFileItemPtr pItem(new CFileItem(movie));
6616         formatter.FormatLabel(pItem.get());
6617       
6618         int idEpisode = record->at(0).get_asInt();
6619
6620         CVideoDbUrl itemUrl = videoUrl;
6621         CStdString path;
6622         if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6623           path = StringUtils::Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6624         else
6625           path = StringUtils::Format("%ld", idEpisode);
6626         itemUrl.AppendPath(path);
6627         pItem->SetPath(itemUrl.ToString());
6628
6629         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6630         pItem->m_dateTime = movie.m_firstAired;
6631         pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6632         items.Add(pItem);
6633       }
6634     }
6635
6636     // cleanup
6637     m_pDS->close();
6638     return true;
6639   }
6640   catch (...)
6641   {
6642     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6643   }
6644   return false;
6645 }
6646
6647 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() */)
6648 {
6649   CVideoDbUrl videoUrl;
6650   if (!videoUrl.FromString(strBaseDir))
6651     return false;
6652
6653   if (idGenre != -1)
6654     videoUrl.AddOption("genreid", idGenre);
6655   else if (idStudio != -1)
6656     videoUrl.AddOption("studioid", idStudio);
6657   else if (idDirector != -1)
6658     videoUrl.AddOption("directorid", idDirector);
6659   else if (idYear !=-1)
6660     videoUrl.AddOption("year", idYear);
6661   else if (idArtist != -1)
6662     videoUrl.AddOption("artistid", idArtist);
6663   else if (idTag != -1)
6664     videoUrl.AddOption("tagid", idTag);
6665   if (idAlbum != -1)
6666     videoUrl.AddOption("albumid", idAlbum);
6667
6668   Filter filter;
6669   return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6670 }
6671
6672 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6673 {
6674   Filter filter;
6675   filter.order = "dateAdded desc, idMovie desc";
6676   filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6677   return GetMoviesByWhere(strBaseDir, filter, items);
6678 }
6679
6680 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6681 {
6682   Filter filter;
6683   filter.order = "dateAdded desc, idEpisode desc";
6684   filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6685   return GetEpisodesByWhere(strBaseDir, filter, items, false);
6686 }
6687
6688 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6689 {
6690   Filter filter;
6691   filter.order = "dateAdded desc, idMVideo desc";
6692   filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6693   return GetMusicVideosByWhere(strBaseDir, filter, items);
6694 }
6695
6696 CStdString CVideoDatabase::GetGenreById(int id)
6697 {
6698   return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6699 }
6700
6701 CStdString CVideoDatabase::GetCountryById(int id)
6702 {
6703   return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6704 }
6705
6706 CStdString CVideoDatabase::GetSetById(int id)
6707 {
6708   return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6709 }
6710
6711 CStdString CVideoDatabase::GetTagById(int id)
6712 {
6713   return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6714 }
6715
6716 CStdString CVideoDatabase::GetPersonById(int id)
6717 {
6718   return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6719 }
6720
6721 CStdString CVideoDatabase::GetStudioById(int id)
6722 {
6723   return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6724 }
6725
6726 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6727 {
6728   return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6729 }
6730
6731 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6732 {
6733   return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6734 }
6735
6736 bool CVideoDatabase::HasSets() const
6737 {
6738   try
6739   {
6740     if (NULL == m_pDB.get()) return false;
6741     if (NULL == m_pDS.get()) return false;
6742
6743     m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6744                  "JOIN sets ON sets.idSet = movieview.idSet "
6745                  "GROUP BY movieview.idSet HAVING c>1");
6746
6747     bool bResult = (m_pDS->num_rows() > 0);
6748     m_pDS->close();
6749     return bResult;
6750   }
6751   catch (...)
6752   {
6753     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6754   }
6755   return false;
6756 }
6757
6758 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6759 {
6760   try
6761   {
6762     if (NULL == m_pDB.get()) return false;
6763     if (NULL == m_pDS2.get()) return false;
6764
6765     // make sure we use m_pDS2, as this is called in loops using m_pDS
6766     CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6767     m_pDS2->query( strSQL.c_str() );
6768
6769     int result=-1;
6770     if (!m_pDS2->eof())
6771       result=m_pDS2->fv(0).get_asInt();
6772     m_pDS2->close();
6773
6774     return result;
6775   }
6776   catch (...)
6777   {
6778     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6779   }
6780   return false;
6781 }
6782
6783 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6784 {
6785   char column[5];
6786   sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6787   CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6788   if (id.empty())
6789     return -1;
6790   return atoi(id.c_str());
6791 }
6792
6793 bool CVideoDatabase::HasContent()
6794 {
6795   return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6796           HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6797           HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6798 }
6799
6800 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6801 {
6802   bool result = false;
6803   try
6804   {
6805     if (NULL == m_pDB.get()) return false;
6806     if (NULL == m_pDS.get()) return false;
6807
6808     CStdString sql;
6809     if (type == VIDEODB_CONTENT_MOVIES)
6810       sql = "select count(1) from movie";
6811     else if (type == VIDEODB_CONTENT_TVSHOWS)
6812       sql = "select count(1) from tvshow";
6813     else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6814       sql = "select count(1) from musicvideo";
6815     m_pDS->query( sql.c_str() );
6816
6817     if (!m_pDS->eof())
6818       result = (m_pDS->fv(0).get_asInt() > 0);
6819
6820     m_pDS->close();
6821   }
6822   catch (...)
6823   {
6824     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6825   }
6826   return result;
6827 }
6828
6829 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6830 {
6831   try
6832   {
6833     if (NULL == m_pDB.get()) return 0;
6834     if (NULL == m_pDS.get()) return 0;
6835
6836     CStdString strSQL = StringUtils::Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6837     m_pDS->query( strSQL.c_str() );
6838
6839     int iResult = 0;
6840     if (!m_pDS->eof())
6841       iResult = m_pDS->fv("nummovies").get_asInt();
6842
6843     m_pDS->close();
6844     return iResult;
6845   }
6846   catch (...)
6847   {
6848     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6849   }
6850   return 0;
6851 }
6852
6853 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6854 {
6855   SScanSettings settings;
6856   return GetScraperForPath(strPath, settings);
6857 }
6858
6859 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6860 {
6861   bool dummy;
6862   return GetScraperForPath(strPath, settings, dummy);
6863 }
6864
6865 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6866 {
6867   foundDirectly = false;
6868   try
6869   {
6870     if (strPath.empty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6871
6872     ScraperPtr scraper;
6873     CStdString strPath2;
6874
6875     if (URIUtils::IsMultiPath(strPath))
6876       strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6877     else
6878       strPath2 = strPath;
6879
6880     CStdString strPath1 = URIUtils::GetDirectory(strPath2);
6881     int idPath = GetPathId(strPath1);
6882
6883     if (idPath > -1)
6884     {
6885       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);
6886       m_pDS->query( strSQL.c_str() );
6887     }
6888
6889     int iFound = 1;
6890     CONTENT_TYPE content = CONTENT_NONE;
6891     if (!m_pDS->eof())
6892     { // path is stored in db
6893
6894       if (m_pDS->fv("path.exclude").get_asBool())
6895       {
6896         settings.exclude = true;
6897         m_pDS->close();
6898         return ScraperPtr();
6899       }
6900       settings.exclude = false;
6901
6902       // try and ascertain scraper for this path
6903       CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6904       strcontent.ToLower();
6905       content = TranslateContent(strcontent);
6906
6907       //FIXME paths stored should not have empty strContent
6908       //assert(content != CONTENT_NONE);
6909       CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6910
6911       AddonPtr addon;
6912       if (!scraperID.empty() &&
6913         CAddonMgr::Get().GetAddon(scraperID, addon))
6914       {
6915         scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6916         if (!scraper)
6917           return ScraperPtr();
6918
6919         // store this path's content & settings
6920         scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6921         settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6922         settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6923         settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6924       }
6925     }
6926
6927     if (content == CONTENT_NONE)
6928     { // this path is not saved in db
6929       // we must drill up until a scraper is configured
6930       CStdString strParent;
6931       while (URIUtils::GetParentPath(strPath1, strParent))
6932       {
6933         iFound++;
6934
6935         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());
6936         m_pDS->query(strSQL.c_str());
6937
6938         CONTENT_TYPE content = CONTENT_NONE;
6939         if (!m_pDS->eof())
6940         {
6941
6942           CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6943           strcontent.ToLower();
6944           if (m_pDS->fv("path.exclude").get_asBool())
6945           {
6946             settings.exclude = true;
6947             scraper.reset();
6948             m_pDS->close();
6949             break;
6950           }
6951
6952           content = TranslateContent(strcontent);
6953
6954           AddonPtr addon;
6955           if (content != CONTENT_NONE &&
6956               CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6957           {
6958             scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6959             scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6960             settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6961             settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6962             settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6963             settings.exclude = false;
6964             break;
6965           }
6966         }
6967         strPath1 = strParent;
6968       }
6969     }
6970     m_pDS->close();
6971
6972     if (!scraper || scraper->Content() == CONTENT_NONE)
6973       return ScraperPtr();
6974
6975     if (scraper->Content() == CONTENT_TVSHOWS)
6976     {
6977       settings.recurse = 0;
6978       if(settings.parent_name) // single show
6979       {
6980         settings.parent_name_root = settings.parent_name = (iFound == 1);
6981       }
6982       else // show root
6983       {
6984         settings.parent_name_root = settings.parent_name = (iFound == 2);
6985       }
6986     }
6987     else if (scraper->Content() == CONTENT_MOVIES)
6988     {
6989       settings.recurse = settings.recurse - (iFound-1);
6990       settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6991     }
6992     else if (scraper->Content() == CONTENT_MUSICVIDEOS)
6993     {
6994       settings.recurse = settings.recurse - (iFound-1);
6995       settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6996     }
6997     else
6998     {
6999       iFound = 0;
7000       return ScraperPtr();
7001     }
7002     foundDirectly = (iFound == 1);
7003     return scraper;
7004   }
7005   catch (...)
7006   {
7007     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7008   }
7009   return ScraperPtr();
7010 }
7011
7012 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
7013 {
7014   SScanSettings settings;
7015   bool foundDirectly = false;
7016   ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
7017   if (scraper)
7018   {
7019     if (scraper->Content() == CONTENT_TVSHOWS)
7020     { // check for episodes or seasons.  Assumptions are:
7021       // 1. if episodes are in the path then we're in episodes.
7022       // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
7023       // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
7024       CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
7025       m_pDS->query( sql.c_str() );
7026       if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
7027         return "episodes";
7028       return foundDirectly ? "tvshows" : "seasons";
7029     }
7030     return TranslateContent(scraper->Content());
7031   }
7032   return "";
7033 }
7034
7035 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
7036 {
7037   CStdString strSQL;
7038
7039   try
7040   {
7041     if (NULL == m_pDB.get()) return;
7042     if (NULL == m_pDS.get()) return;
7043
7044     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7045       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());
7046     else
7047       strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
7048     m_pDS->query( strSQL.c_str() );
7049
7050     while (!m_pDS->eof())
7051     {
7052       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7053         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
7054                                                       *CMediaSourceSettings::Get().GetSources("video")))
7055         {
7056           m_pDS->next();
7057           continue;
7058         }
7059
7060       CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7061       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7062       pItem->SetPath("videodb://movies/genres/"+ strDir);
7063       pItem->m_bIsFolder=true;
7064       items.Add(pItem);
7065       m_pDS->next();
7066     }
7067     m_pDS->close();
7068   }
7069   catch (...)
7070   {
7071     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7072   }
7073 }
7074
7075 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
7076 {
7077   CStdString strSQL;
7078
7079   try
7080   {
7081     if (NULL == m_pDB.get()) return;
7082     if (NULL == m_pDS.get()) return;
7083
7084     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7085       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());
7086     else
7087       strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
7088     m_pDS->query( strSQL.c_str() );
7089
7090     while (!m_pDS->eof())
7091     {
7092       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7093         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
7094                                                       *CMediaSourceSettings::Get().GetSources("video")))
7095         {
7096           m_pDS->next();
7097           continue;
7098         }
7099
7100       CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
7101       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
7102       pItem->SetPath("videodb://movies/genres/"+ strDir);
7103       pItem->m_bIsFolder=true;
7104       items.Add(pItem);
7105       m_pDS->next();
7106     }
7107     m_pDS->close();
7108   }
7109   catch (...)
7110   {
7111     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7112   }
7113 }
7114
7115 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
7116 {
7117   CStdString strSQL;
7118
7119   try
7120   {
7121     if (NULL == m_pDB.get()) return;
7122     if (NULL == m_pDS.get()) return;
7123
7124     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7125       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());
7126     else
7127       strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
7128     m_pDS->query( strSQL.c_str() );
7129
7130     while (!m_pDS->eof())
7131     {
7132       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7133         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7134         {
7135           m_pDS->next();
7136           continue;
7137         }
7138
7139       CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7140       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7141       pItem->SetPath("videodb://tvshows/genres/"+ strDir);
7142       pItem->m_bIsFolder=true;
7143       items.Add(pItem);
7144       m_pDS->next();
7145     }
7146     m_pDS->close();
7147   }
7148   catch (...)
7149   {
7150     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7151   }
7152 }
7153
7154 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
7155 {
7156   CStdString strSQL;
7157
7158   try
7159   {
7160     if (NULL == m_pDB.get()) return;
7161     if (NULL == m_pDS.get()) return;
7162
7163     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7164       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());
7165     else
7166       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());
7167     m_pDS->query( strSQL.c_str() );
7168
7169     while (!m_pDS->eof())
7170     {
7171       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7172         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7173         {
7174           m_pDS->next();
7175           continue;
7176         }
7177
7178       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7179       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7180       pItem->SetPath("videodb://movies/actors/"+ strDir);
7181       pItem->m_bIsFolder=true;
7182       items.Add(pItem);
7183       m_pDS->next();
7184     }
7185     m_pDS->close();
7186   }
7187   catch (...)
7188   {
7189     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7190   }
7191 }
7192
7193 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
7194 {
7195   CStdString strSQL;
7196
7197   try
7198   {
7199     if (NULL == m_pDB.get()) return;
7200     if (NULL == m_pDS.get()) return;
7201
7202     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7203       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());
7204     else
7205       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());
7206     m_pDS->query( strSQL.c_str() );
7207
7208     while (!m_pDS->eof())
7209     {
7210       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7211         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7212         {
7213           m_pDS->next();
7214           continue;
7215         }
7216
7217       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7218       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7219       pItem->SetPath("videodb://tvshows/actors/"+ strDir);
7220       pItem->m_bIsFolder=true;
7221       items.Add(pItem);
7222       m_pDS->next();
7223     }
7224     m_pDS->close();
7225   }
7226   catch (...)
7227   {
7228     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7229   }
7230 }
7231
7232 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
7233 {
7234   CStdString strSQL;
7235
7236   try
7237   {
7238     if (NULL == m_pDB.get()) return;
7239     if (NULL == m_pDS.get()) return;
7240
7241     CStdString strLike;
7242     if (!strSearch.empty())
7243       strLike = "and actors.strActor like '%%%s%%'";
7244     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7245       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());
7246     else
7247       strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
7248     m_pDS->query( strSQL.c_str() );
7249
7250     while (!m_pDS->eof())
7251     {
7252       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7253         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7254         {
7255           m_pDS->next();
7256           continue;
7257         }
7258
7259       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7260       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7261       pItem->SetPath("videodb://musicvideos/artists/"+ strDir);
7262       pItem->m_bIsFolder=true;
7263       items.Add(pItem);
7264       m_pDS->next();
7265     }
7266     m_pDS->close();
7267   }
7268   catch (...)
7269   {
7270     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7271   }
7272 }
7273
7274 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
7275 {
7276   CStdString strSQL;
7277
7278   try
7279   {
7280     if (NULL == m_pDB.get()) return;
7281     if (NULL == m_pDS.get()) return;
7282
7283     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7284       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());
7285     else
7286       strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
7287     m_pDS->query( strSQL.c_str() );
7288
7289     while (!m_pDS->eof())
7290     {
7291       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7292         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7293         {
7294           m_pDS->next();
7295           continue;
7296         }
7297
7298       CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7299       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7300       pItem->SetPath("videodb://musicvideos/genres/"+ strDir);
7301       pItem->m_bIsFolder=true;
7302       items.Add(pItem);
7303       m_pDS->next();
7304     }
7305     m_pDS->close();
7306   }
7307   catch (...)
7308   {
7309     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7310   }
7311 }
7312
7313 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7314 {
7315   CStdString strSQL;
7316
7317   try
7318   {
7319     if (NULL == m_pDB.get()) return;
7320     if (NULL == m_pDS.get()) return;
7321
7322     CStdString strLike;
7323     if (!strSearch.empty())
7324     {
7325       strLike = StringUtils::Format("and musicvideo.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
7326       strLike += "like '%%s%%%'";
7327     }
7328     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7329       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());
7330     else
7331     {
7332       if (!strLike.empty())
7333         strLike = "where "+strLike.Mid(4);
7334       strSQL=PrepareSQL("select distinct musicvideo.c%02d,musicvideo.idMVideo from musicvideo"+strLike,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7335     }
7336     m_pDS->query( strSQL.c_str() );
7337
7338     while (!m_pDS->eof())
7339     {
7340       if (m_pDS->fv(0).get_asString().empty())
7341       {
7342         m_pDS->next();
7343         continue;
7344       }
7345
7346       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7347         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7348         {
7349           m_pDS->next();
7350           continue;
7351         }
7352
7353       CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7354       CStdString strDir = StringUtils::Format("%ld", m_pDS->fv(1).get_asInt());
7355       pItem->SetPath("videodb://musicvideos/titles/"+ strDir);
7356       pItem->m_bIsFolder=false;
7357       items.Add(pItem);
7358       m_pDS->next();
7359     }
7360     m_pDS->close();
7361   }
7362   catch (...)
7363   {
7364     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7365   }
7366 }
7367
7368 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7369 {
7370   CStdString strSQL;
7371
7372   try
7373   {
7374     if (NULL == m_pDB.get()) return;
7375     if (NULL == m_pDS.get()) return;
7376
7377     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7378       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());
7379     else
7380       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());
7381     m_pDS->query( strSQL.c_str() );
7382
7383     while (!m_pDS->eof())
7384     {
7385       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7386         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7387         {
7388           m_pDS->next();
7389           continue;
7390         }
7391
7392       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7393       CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7394
7395       pItem->SetPath("videodb://"+ strDir);
7396       pItem->m_bIsFolder=false;
7397       items.Add(pItem);
7398       m_pDS->next();
7399     }
7400     m_pDS->close();
7401   }
7402   catch (...)
7403   {
7404     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7405   }
7406 }
7407
7408 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7409 {
7410   try
7411   {
7412     movieTime = 0;
7413     castTime = 0;
7414
7415     if (NULL == m_pDB.get()) return false;
7416     if (NULL == m_pDS.get()) return false;
7417
7418     int total = -1;
7419     
7420     CStdString strSQL = "select %s from musicvideoview ";
7421     CVideoDbUrl videoUrl;
7422     CStdString strSQLExtra;
7423     Filter extFilter = filter;
7424     SortDescription sorting = sortDescription;
7425     if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7426       return false;
7427
7428     // Apply the limiting directly here if there's no special sorting but limiting
7429     if (extFilter.limit.empty() &&
7430       sorting.sortBy == SortByNone &&
7431       (sorting.limitStart > 0 || sorting.limitEnd > 0))
7432     {
7433       total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7434       strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7435     }
7436
7437     strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7438
7439     int iRowsFound = RunQuery(strSQL);
7440     if (iRowsFound <= 0)
7441       return iRowsFound == 0;
7442
7443     // store the total value of items as a property
7444     if (total < iRowsFound)
7445       total = iRowsFound;
7446     items.SetProperty("total", total);
7447     
7448     DatabaseResults results;
7449     results.reserve(iRowsFound);
7450     if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7451       return false;
7452     
7453     // get data from returned rows
7454     items.Reserve(results.size());
7455     // get songs from returned subtable
7456     const query_data &data = m_pDS->get_result_set().records;
7457     for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7458     {
7459       unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7460       const dbiplus::sql_record* const record = data.at(targetRow);
7461       
7462       CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7463       if (!checkLocks || CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7464           g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
7465       {
7466         CFileItemPtr item(new CFileItem(musicvideo));
7467
7468         CVideoDbUrl itemUrl = videoUrl;
7469         CStdString path = StringUtils::Format("%ld", record->at(0).get_asInt());
7470         itemUrl.AppendPath(path);
7471         item->SetPath(itemUrl.ToString());
7472
7473         item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7474         items.Add(item);
7475       }
7476     }
7477
7478     // cleanup
7479     m_pDS->close();
7480     return true;
7481   }
7482   catch (...)
7483   {
7484     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7485   }
7486   return false;
7487 }
7488
7489 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7490 {
7491   try
7492   {
7493     if (NULL == m_pDB.get()) return 0;
7494     if (NULL == m_pDS.get()) return 0;
7495
7496     CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7497     if (!m_pDS->query(strSQL.c_str())) return 0;
7498     songIDs.clear();
7499     if (m_pDS->num_rows() == 0)
7500     {
7501       m_pDS->close();
7502       return 0;
7503     }
7504     songIDs.reserve(m_pDS->num_rows());
7505     while (!m_pDS->eof())
7506     {
7507       songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7508       m_pDS->next();
7509     }    // cleanup
7510     m_pDS->close();
7511     return songIDs.size();
7512   }
7513   catch (...)
7514   {
7515     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7516   }
7517   return 0;
7518 }
7519
7520 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7521 {
7522   try
7523   {
7524     idSong = -1;
7525
7526     if (NULL == m_pDB.get()) return false;
7527     if (NULL == m_pDS.get()) return false;
7528
7529     // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7530     CStdString strSQL = StringUtils::Format("select * from musicvideoview where %s", strWhere.c_str());
7531     strSQL += PrepareSQL(" order by RANDOM() limit 1");
7532     CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7533     // run query
7534     if (!m_pDS->query(strSQL.c_str()))
7535       return false;
7536     int iRowsFound = m_pDS->num_rows();
7537     if (iRowsFound != 1)
7538     {
7539       m_pDS->close();
7540       return false;
7541     }
7542     *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7543     CStdString path = StringUtils::Format("videodb://musicvideos/titles/%ld",item->GetVideoInfoTag()->m_iDbId);
7544     item->SetPath(path);
7545     idSong = m_pDS->fv("idMVideo").get_asInt();
7546     item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7547     m_pDS->close();
7548     return true;
7549   }
7550   catch(...)
7551   {
7552     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7553   }
7554   return false;
7555 }
7556
7557 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7558 {
7559   try
7560   {
7561     if (NULL == m_pDB.get()) return -1;
7562     if (NULL == m_pDS.get()) return -1;
7563
7564     CStdString strSQL;
7565     if (strAlbum.empty() && strTitle.empty())
7566     { // we want to return matching artists only
7567       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7568         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());
7569       else
7570         strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7571     }
7572     else
7573     { // we want to return the matching musicvideo
7574       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7575         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());
7576       else
7577         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());
7578     }
7579     m_pDS->query( strSQL.c_str() );
7580
7581     if (m_pDS->eof())
7582       return -1;
7583
7584     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7585       if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7586       {
7587         m_pDS->close();
7588         return -1;
7589       }
7590
7591     int lResult = m_pDS->fv(0).get_asInt();
7592     m_pDS->close();
7593     return lResult;
7594   }
7595   catch (...)
7596   {
7597     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7598   }
7599   return -1;
7600 }
7601
7602 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7603 {
7604   CStdString strSQL;
7605
7606   try
7607   {
7608     if (NULL == m_pDB.get()) return;
7609     if (NULL == m_pDS.get()) return;
7610
7611     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7612       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());
7613     else
7614       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());
7615     m_pDS->query( strSQL.c_str() );
7616
7617     while (!m_pDS->eof())
7618     {
7619       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7620         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7621         {
7622           m_pDS->next();
7623           continue;
7624         }
7625
7626       int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7627       int setId = m_pDS->fv("movie.idSet").get_asInt();
7628       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7629       CStdString path;
7630       if (setId <= 0 || !CSettings::Get().GetBool("videolibrary.groupmoviesets"))
7631         path = StringUtils::Format("videodb://movies/titles/%i", movieId);
7632       else
7633         path = StringUtils::Format("videodb://movies/sets/%i/%i", setId, movieId);
7634       pItem->SetPath(path);
7635       pItem->m_bIsFolder=false;
7636       items.Add(pItem);
7637       m_pDS->next();
7638     }
7639     m_pDS->close();
7640   }
7641   catch (...)
7642   {
7643     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7644   }
7645 }
7646
7647 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7648 {
7649   CStdString strSQL;
7650
7651   try
7652   {
7653     if (NULL == m_pDB.get()) return;
7654     if (NULL == m_pDS.get()) return;
7655
7656     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7657       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());
7658     else
7659       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());
7660     m_pDS->query( strSQL.c_str() );
7661
7662     while (!m_pDS->eof())
7663     {
7664       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7665         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7666         {
7667           m_pDS->next();
7668           continue;
7669         }
7670
7671       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7672       CStdString strDir = StringUtils::Format("tvshows/titles/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7673
7674       pItem->SetPath("videodb://"+ strDir);
7675       pItem->m_bIsFolder=true;
7676       pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7677       items.Add(pItem);
7678       m_pDS->next();
7679     }
7680     m_pDS->close();
7681   }
7682   catch (...)
7683   {
7684     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7685   }
7686 }
7687
7688 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7689 {
7690   CStdString strSQL;
7691
7692   try
7693   {
7694     if (NULL == m_pDB.get()) return;
7695     if (NULL == m_pDS.get()) return;
7696
7697     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7698       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());
7699     else
7700       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());
7701     m_pDS->query( strSQL.c_str() );
7702
7703     while (!m_pDS->eof())
7704     {
7705       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7706         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7707         {
7708           m_pDS->next();
7709           continue;
7710         }
7711
7712       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7713       CStdString path = StringUtils::Format("videodb://tvshows/titles/%ld/%ld/%ld",m_pDS->fv("episode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
7714       pItem->SetPath(path);
7715       pItem->m_bIsFolder=false;
7716       items.Add(pItem);
7717       m_pDS->next();
7718     }
7719     m_pDS->close();
7720   }
7721   catch (...)
7722   {
7723     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7724   }
7725 }
7726
7727 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7728 {
7729 // Alternative searching - not quite as fast though due to
7730 // retrieving all information
7731 //  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()));
7732 //  GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
7733   CStdString strSQL;
7734
7735   try
7736   {
7737     if (NULL == m_pDB.get()) return;
7738     if (NULL == m_pDS.get()) return;
7739
7740     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7741       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());
7742     else
7743       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());
7744     m_pDS->query( strSQL.c_str() );
7745
7746     while (!m_pDS->eof())
7747     {
7748       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7749         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7750         {
7751           m_pDS->next();
7752           continue;
7753         }
7754
7755       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7756       CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7757
7758       pItem->SetPath("videodb://"+ strDir);
7759       pItem->m_bIsFolder=false;
7760       items.Add(pItem);
7761       m_pDS->next();
7762     }
7763     m_pDS->close();
7764   }
7765   catch (...)
7766   {
7767     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7768   }
7769 }
7770
7771 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7772 {
7773 // Alternative searching - not quite as fast though due to
7774 // retrieving all information
7775 //  Filter filter;
7776 //  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());
7777 //  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());
7778 //  GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
7779 //  return;
7780   CStdString strSQL;
7781
7782   try
7783   {
7784     if (NULL == m_pDB.get()) return;
7785     if (NULL == m_pDS.get()) return;
7786
7787     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7788       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());
7789     else
7790       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());
7791     m_pDS->query( strSQL.c_str() );
7792
7793     while (!m_pDS->eof())
7794     {
7795       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7796         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7797         {
7798           m_pDS->next();
7799           continue;
7800         }
7801
7802       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7803       CStdString path = StringUtils::Format("videodb://tvshows/titles/%ld/%ld/%ld",m_pDS->fv("episode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
7804       pItem->SetPath(path);
7805       pItem->m_bIsFolder=false;
7806       items.Add(pItem);
7807       m_pDS->next();
7808     }
7809     m_pDS->close();
7810   }
7811   catch (...)
7812   {
7813     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7814   }
7815 }
7816
7817 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7818 {
7819   CStdString strSQL;
7820
7821   try
7822   {
7823     if (NULL == m_pDB.get()) return;
7824     if (NULL == m_pDS.get()) return;
7825
7826     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7827       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());
7828     else
7829       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());
7830
7831     m_pDS->query( strSQL.c_str() );
7832
7833     while (!m_pDS->eof())
7834     {
7835       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7836         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7837         {
7838           m_pDS->next();
7839           continue;
7840         }
7841
7842       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7843       CStdString path = StringUtils::Format("videodb://movies/titles/%ld", m_pDS->fv(0).get_asInt());
7844       pItem->SetPath(path);
7845       pItem->m_bIsFolder=false;
7846
7847       items.Add(pItem);
7848       m_pDS->next();
7849     }
7850     m_pDS->close();
7851
7852   }
7853   catch (...)
7854   {
7855     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7856   }
7857 }
7858
7859 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7860 {
7861   CStdString strSQL;
7862
7863   try
7864   {
7865     if (NULL == m_pDB.get()) return;
7866     if (NULL == m_pDS.get()) return;
7867
7868     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7869       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());
7870     else
7871       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());
7872
7873     m_pDS->query( strSQL.c_str() );
7874
7875     while (!m_pDS->eof())
7876     {
7877       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7878         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7879         {
7880           m_pDS->next();
7881           continue;
7882         }
7883
7884       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7885       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7886
7887       pItem->SetPath("videodb://movies/directors/"+ strDir);
7888       pItem->m_bIsFolder=true;
7889       items.Add(pItem);
7890       m_pDS->next();
7891     }
7892     m_pDS->close();
7893   }
7894   catch (...)
7895   {
7896     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7897   }
7898 }
7899
7900 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7901 {
7902   CStdString strSQL;
7903
7904   try
7905   {
7906     if (NULL == m_pDB.get()) return;
7907     if (NULL == m_pDS.get()) return;
7908
7909     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7910       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());
7911     else
7912       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());
7913
7914     m_pDS->query( strSQL.c_str() );
7915
7916     while (!m_pDS->eof())
7917     {
7918       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7919         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7920         {
7921           m_pDS->next();
7922           continue;
7923         }
7924
7925       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7926       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7927
7928       pItem->SetPath("videodb://tvshows/studios/"+ strDir);
7929       pItem->m_bIsFolder=true;
7930       items.Add(pItem);
7931       m_pDS->next();
7932     }
7933     m_pDS->close();
7934   }
7935   catch (...)
7936   {
7937     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7938   }
7939 }
7940
7941 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7942 {
7943   CStdString strSQL;
7944
7945   try
7946   {
7947     if (NULL == m_pDB.get()) return;
7948     if (NULL == m_pDS.get()) return;
7949
7950     if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7951       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());
7952     else
7953       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());
7954
7955     m_pDS->query( strSQL.c_str() );
7956
7957     while (!m_pDS->eof())
7958     {
7959       if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7960         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7961         {
7962           m_pDS->next();
7963           continue;
7964         }
7965
7966       CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7967       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7968
7969       pItem->SetPath("videodb://musicvideos/albums/"+ strDir);
7970       pItem->m_bIsFolder=true;
7971       items.Add(pItem);
7972       m_pDS->next();
7973     }
7974     m_pDS->close();
7975   }
7976   catch (...)
7977   {
7978     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7979   }
7980 }
7981
7982 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7983 {
7984   CGUIDialogProgress *progress=NULL;
7985   try
7986   {
7987     if (NULL == m_pDB.get()) return;
7988     if (NULL == m_pDS.get()) return;
7989
7990     unsigned int time = XbmcThreads::SystemClockMillis();
7991     CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
7992     ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
7993
7994     BeginTransaction();
7995
7996     // find all the files
7997     CStdString sql;
7998     if (paths)
7999     {
8000       if (paths->size() == 0)
8001       {
8002         RollbackTransaction();
8003         ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8004         return;
8005       }
8006
8007       CStdString strPaths;
8008       for (std::set<int>::const_iterator i = paths->begin(); i != paths->end(); ++i)
8009         strPaths.AppendFormat(",%i",*i);
8010       sql = PrepareSQL("select * from files,path where files.idPath=path.idPath and path.idPath in (%s)",strPaths.Mid(1).c_str());
8011     }
8012     else
8013       sql = "select * from files, path where files.idPath = path.idPath";
8014
8015     m_pDS->query(sql.c_str());
8016     if (m_pDS->num_rows() == 0) return;
8017
8018     if (handle)
8019     {
8020       handle->SetTitle(g_localizeStrings.Get(700));
8021       handle->SetText("");
8022     }
8023     else if (showProgress)
8024     {
8025       progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8026       if (progress)
8027       {
8028         progress->SetHeading(700);
8029         progress->SetLine(0, "");
8030         progress->SetLine(1, 313);
8031         progress->SetLine(2, 330);
8032         progress->SetPercentage(0);
8033         progress->StartModal();
8034         progress->ShowProgressBar(true);
8035       }
8036     }
8037
8038     CStdString filesToDelete = "";
8039     CStdString moviesToDelete = "";
8040     CStdString episodesToDelete = "";
8041     CStdString musicVideosToDelete = "";
8042
8043     std::vector<int> movieIDs;
8044     std::vector<int> episodeIDs;
8045     std::vector<int> musicVideoIDs;
8046
8047     int total = m_pDS->num_rows();
8048     int current = 0;
8049
8050     while (!m_pDS->eof())
8051     {
8052       CStdString path = m_pDS->fv("path.strPath").get_asString();
8053       CStdString fileName = m_pDS->fv("files.strFileName").get_asString();
8054       CStdString fullPath;
8055       ConstructPath(fullPath,path,fileName);
8056
8057       // get the first stacked file
8058       if (URIUtils::IsStack(fullPath))
8059         fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
8060
8061       // remove optical, non-existing files
8062       if (URIUtils::IsOnDVD(fullPath) || !CFile::Exists(fullPath, false))
8063         filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
8064
8065       if (!handle)
8066       {
8067         if (progress)
8068         {
8069           progress->SetPercentage(current * 100 / total);
8070           progress->Progress();
8071           if (progress->IsCanceled())
8072           {
8073             progress->Close();
8074             m_pDS->close();
8075             ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8076             return;
8077           }
8078         }
8079       }
8080       else
8081         handle->SetPercentage(current/(float)total*100);
8082
8083       m_pDS->next();
8084       current++;
8085     }
8086     m_pDS->close();
8087
8088     // Add any files that don't have a valid idPath entry to the filesToDelete list.
8089     sql = "select files.idFile from files where idPath not in (select idPath from path)";
8090     m_pDS->query(sql.c_str());
8091     while (!m_pDS->eof())
8092     {
8093       filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
8094       m_pDS->next();
8095     }
8096     m_pDS->close();
8097
8098     if ( ! filesToDelete.empty() )
8099     {
8100       filesToDelete.TrimRight(",");
8101       // now grab them movies
8102       sql = PrepareSQL("select idMovie from movie where idFile in (%s)",filesToDelete.c_str());
8103       m_pDS->query(sql.c_str());
8104       while (!m_pDS->eof())
8105       {
8106         movieIDs.push_back(m_pDS->fv(0).get_asInt());
8107         moviesToDelete += m_pDS->fv(0).get_asString() + ",";
8108         m_pDS->next();
8109       }
8110       m_pDS->close();
8111       // now grab them episodes
8112       sql = PrepareSQL("select idEpisode from episode where idFile in (%s)",filesToDelete.c_str());
8113        m_pDS->query(sql.c_str());
8114       while (!m_pDS->eof())
8115       {
8116         episodeIDs.push_back(m_pDS->fv(0).get_asInt());
8117         episodesToDelete += m_pDS->fv(0).get_asString() + ",";
8118         m_pDS->next();
8119       }
8120       m_pDS->close();
8121
8122       // now grab them musicvideos
8123       sql = PrepareSQL("select idMVideo from musicvideo where idFile in (%s)",filesToDelete.c_str());
8124       m_pDS->query(sql.c_str());
8125       while (!m_pDS->eof())
8126       {
8127         musicVideoIDs.push_back(m_pDS->fv(0).get_asInt());
8128         musicVideosToDelete += m_pDS->fv(0).get_asString() + ",";
8129         m_pDS->next();
8130       }
8131       m_pDS->close();
8132     }
8133
8134     if (progress)
8135     {
8136       progress->SetPercentage(100);
8137       progress->Progress();
8138     }
8139
8140     if ( ! filesToDelete.empty() )
8141     {
8142       filesToDelete = "(" + filesToDelete + ")";
8143       CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
8144       sql = "delete from files where idFile in " + filesToDelete;
8145       m_pDS->exec(sql.c_str());
8146
8147       CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
8148       sql = "delete from streamdetails where idFile in " + filesToDelete;
8149       m_pDS->exec(sql.c_str());
8150
8151       CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
8152       sql = "delete from bookmark where idFile in " + filesToDelete;
8153       m_pDS->exec(sql.c_str());
8154
8155       CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
8156       sql = "delete from settings where idFile in " + filesToDelete;
8157       m_pDS->exec(sql.c_str());
8158
8159       CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
8160       sql = "delete from stacktimes where idFile in " + filesToDelete;
8161       m_pDS->exec(sql.c_str());
8162     }
8163
8164     if ( ! moviesToDelete.empty() )
8165     {
8166       moviesToDelete = "(" + moviesToDelete.TrimRight(",") + ")";
8167
8168       CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
8169       sql = "delete from movie where idMovie in " + moviesToDelete;
8170       m_pDS->exec(sql.c_str());
8171
8172       CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
8173       sql = "delete from actorlinkmovie where idMovie in " + moviesToDelete;
8174       m_pDS->exec(sql.c_str());
8175
8176       CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
8177       sql = "delete from directorlinkmovie where idMovie in " + moviesToDelete;
8178       m_pDS->exec(sql.c_str());
8179
8180       CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
8181       sql = "delete from writerlinkmovie where idMovie in " + moviesToDelete;
8182       m_pDS->exec(sql.c_str());
8183
8184       CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
8185       sql = "delete from genrelinkmovie where idMovie in " + moviesToDelete;
8186       m_pDS->exec(sql.c_str());
8187
8188       CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
8189       sql = "delete from countrylinkmovie where idMovie in " + moviesToDelete;
8190       m_pDS->exec(sql.c_str());
8191
8192       CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
8193       sql = "delete from studiolinkmovie where idMovie in " + moviesToDelete;
8194       m_pDS->exec(sql.c_str());
8195     }
8196
8197     if ( ! episodesToDelete.empty() )
8198     {
8199       episodesToDelete = "(" + episodesToDelete.TrimRight(",") + ")";
8200
8201       CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
8202       sql = "delete from episode where idEpisode in " + episodesToDelete;
8203       m_pDS->exec(sql.c_str());
8204
8205       CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
8206       sql = "delete from actorlinkepisode where idEpisode in " + episodesToDelete;
8207       m_pDS->exec(sql.c_str());
8208
8209       CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
8210       sql = "delete from directorlinkepisode where idEpisode in " + episodesToDelete;
8211       m_pDS->exec(sql.c_str());
8212
8213       CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
8214       sql = "delete from writerlinkepisode where idEpisode in " + episodesToDelete;
8215       m_pDS->exec(sql.c_str());
8216     }
8217
8218     CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and have content set...", __FUNCTION__);
8219     sql = "select * from path where not (strContent='' and strSettings='' and strHash='' and exclude!=1)";
8220     m_pDS->query(sql.c_str());
8221     CStdString strIds;
8222     while (!m_pDS->eof())
8223     {
8224       if (!CDirectory::Exists(m_pDS->fv("path.strPath").get_asString()))
8225         strIds.AppendFormat("%i,", m_pDS->fv("path.idPath").get_asInt());
8226       m_pDS->next();
8227     }
8228     m_pDS->close();
8229     if (!strIds.empty())
8230     {
8231       strIds.TrimRight(",");
8232       sql = PrepareSQL("delete from path where idPath in (%s)",strIds.c_str());
8233       m_pDS->exec(sql.c_str());
8234       sql = PrepareSQL("delete from tvshowlinkpath where idPath in (%s)",strIds.c_str());
8235       m_pDS->exec(sql.c_str());
8236     }
8237     sql = "delete from tvshowlinkpath where idPath not in (select idPath from path)";
8238     m_pDS->exec(sql.c_str());
8239
8240     CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
8241     sql = "delete from tvshow where idShow not in (select idShow from tvshowlinkpath)";
8242     m_pDS->exec(sql.c_str());
8243
8244     std::vector<int> tvshowIDs;
8245     CStdString showsToDelete;
8246     sql = "select tvshow.idShow from tvshow "
8247             "join tvshowlinkpath on tvshow.idShow=tvshowlinkpath.idShow "
8248             "join path on path.idPath=tvshowlinkpath.idPath "
8249           "where tvshow.idShow not in (select idShow from episode) "
8250             "and path.strContent=''";
8251     m_pDS->query(sql.c_str());
8252     while (!m_pDS->eof())
8253     {
8254       tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
8255       showsToDelete += m_pDS->fv(0).get_asString() + ",";
8256       m_pDS->next();
8257     }
8258     m_pDS->close();
8259     if (!showsToDelete.empty())
8260     {
8261       sql = "delete from tvshow where idShow in (" + showsToDelete.TrimRight(",") + ")";
8262       m_pDS->exec(sql.c_str());
8263     }
8264
8265     CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
8266     sql = "delete from actorlinktvshow where idShow not in (select idShow from tvshow)";
8267     m_pDS->exec(sql.c_str());
8268
8269     CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
8270     sql = "delete from directorlinktvshow where idShow not in (select idShow from tvshow)";
8271     m_pDS->exec(sql.c_str());
8272
8273     CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
8274     sql = "delete from tvshowlinkpath where idShow not in (select idShow from tvshow)";
8275     m_pDS->exec(sql.c_str());
8276
8277     CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
8278     sql = "delete from genrelinktvshow where idShow not in (select idShow from tvshow)";
8279     m_pDS->exec(sql.c_str());
8280
8281     CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
8282     sql = "delete from seasons where idShow not in (select idShow from tvshow)";
8283     m_pDS->exec(sql.c_str());
8284
8285     CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
8286     sql = "delete from movielinktvshow where idShow not in (select idShow from tvshow)";
8287     m_pDS->exec(sql.c_str());
8288     sql = "delete from movielinktvshow where idMovie not in (select distinct idMovie from movie)";
8289     m_pDS->exec(sql.c_str());
8290
8291     if ( ! musicVideosToDelete.empty() )
8292     {
8293       musicVideosToDelete = "(" + musicVideosToDelete.TrimRight(",") + ")";
8294
8295       CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8296       sql = "delete from musicvideo where idMVideo in " + musicVideosToDelete;
8297       m_pDS->exec(sql.c_str());
8298
8299       CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8300       sql = "delete from artistlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8301       m_pDS->exec(sql.c_str());
8302
8303       CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8304       sql = "delete from directorlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8305       m_pDS->exec(sql.c_str());
8306
8307       CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8308       sql = "delete from genrelinkmusicvideo where idMVideo in " + musicVideosToDelete;
8309       m_pDS->exec(sql.c_str());
8310
8311       CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8312       sql = "delete from studiolinkmusicvideo where idMVideo in " + musicVideosToDelete;
8313       m_pDS->exec(sql.c_str());
8314     }
8315
8316     CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8317     sql = StringUtils::Format("delete from path where strContent='' and strSettings='' and strHash='' and exclude!=1 "
8318                                   "and idPath not in (select distinct idPath from files) "
8319                                   "and idPath not in (select distinct idPath from tvshowlinkpath) "
8320                                   "and idPath not in (select distinct c%02d from movie) "
8321                                   "and idPath not in (select distinct c%02d from tvshow) "
8322                                   "and idPath not in (select distinct c%02d from episode) "
8323                                   "and idPath not in (select distinct c%02d from musicvideo)"
8324                 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8325     m_pDS->exec(sql.c_str());
8326
8327     CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8328     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)";
8329     m_pDS->exec(sql.c_str());
8330
8331     CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8332     sql = "delete from country where idCountry not in (select distinct idCountry from countrylinkmovie)";
8333     m_pDS->exec(sql.c_str());
8334
8335     CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8336     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)";
8337     m_pDS->exec(sql.c_str());
8338
8339     CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8340     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)";
8341     m_pDS->exec(sql.c_str());
8342
8343     CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8344     sql = "delete from sets where idSet not in (select distinct idSet from movie)";
8345     m_pDS->exec(sql.c_str());
8346
8347     CommitTransaction();
8348
8349     if (handle)
8350       handle->SetTitle(g_localizeStrings.Get(331));
8351
8352     Compress(false);
8353
8354     CUtil::DeleteVideoDatabaseDirectoryCache();
8355
8356     time = XbmcThreads::SystemClockMillis() - time;
8357     CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8358
8359     for (unsigned int i = 0; i < movieIDs.size(); i++)
8360       AnnounceRemove("movie", movieIDs[i]);
8361
8362     for (unsigned int i = 0; i < episodeIDs.size(); i++)
8363       AnnounceRemove("episode", episodeIDs[i]);
8364
8365     for (unsigned int i = 0; i < tvshowIDs.size(); i++)
8366       AnnounceRemove("tvshow", tvshowIDs[i]);
8367
8368     for (unsigned int i = 0; i < musicVideoIDs.size(); i++)
8369       AnnounceRemove("musicvideo", musicVideoIDs[i]);
8370   }
8371   catch (...)
8372   {
8373     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8374     RollbackTransaction();
8375   }
8376   if (progress)
8377     progress->Close();
8378
8379   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8380 }
8381
8382 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8383 {
8384   // get all tvshows
8385   CFileItemList items;
8386   GetTvShowsByWhere("videodb://tvshows/titles/", "", items);
8387   CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8388   CDirectory::Create(showPath);
8389   for (int i = 0; i < items.Size(); i++)
8390   {
8391     // create a folder in this directory
8392     CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8393     CStdString TVFolder = URIUtils::AddFileToFolder(showPath, showName);
8394     if (CDirectory::Create(TVFolder))
8395     { // right - grab the episodes and dump them as well
8396       CFileItemList episodes;
8397       Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8398       GetEpisodesByWhere("videodb://tvshows/titles/", filter, episodes);
8399       for (int i = 0; i < episodes.Size(); i++)
8400       {
8401         CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8402         CStdString episode = StringUtils::Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8403         // and make a file
8404         CStdString episodePath = URIUtils::AddFileToFolder(TVFolder, episode);
8405         CFile file;
8406         if (file.OpenForWrite(episodePath))
8407           file.Close();
8408       }
8409     }
8410   }
8411   // get all movies
8412   items.Clear();
8413   GetMoviesByWhere("videodb://movies/titles/", "", items);
8414   CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8415   CDirectory::Create(moviePath);
8416   for (int i = 0; i < items.Size(); i++)
8417   {
8418     CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8419     CStdString movie = StringUtils::Format("%s.avi", tag->m_strTitle.c_str());
8420     CFile file;
8421     if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8422       file.Close();
8423   }
8424 }
8425
8426 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8427 {
8428   CGUIDialogProgress *progress=NULL;
8429   try
8430   {
8431     if (NULL == m_pDB.get()) return;
8432     if (NULL == m_pDS.get()) return;
8433     if (NULL == m_pDS2.get()) return;
8434
8435     // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8436     auto_ptr<Dataset> pDS;
8437     pDS.reset(m_pDB->CreateDataset());
8438     if (NULL == pDS.get()) return;
8439
8440     auto_ptr<Dataset> pDS2;
8441     pDS2.reset(m_pDB->CreateDataset());
8442     if (NULL == pDS2.get()) return;
8443
8444     // if we're exporting to a single folder, we export thumbs as well
8445     CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8446     CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8447     CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8448     CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8449     CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8450     CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8451     if (!singleFiles)
8452     {
8453       images = true;
8454       overwrite = false;
8455       actorThumbs = true;
8456       CDirectory::Remove(exportRoot);
8457       CDirectory::Create(exportRoot);
8458       CDirectory::Create(actorsDir);
8459       CDirectory::Create(moviesDir);
8460       CDirectory::Create(musicvideosDir);
8461       CDirectory::Create(tvshowsDir);
8462     }
8463
8464     progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8465     // find all movies
8466     CStdString sql = "select * from movieview";
8467
8468     m_pDS->query(sql.c_str());
8469
8470     if (progress)
8471     {
8472       progress->SetHeading(647);
8473       progress->SetLine(0, 650);
8474       progress->SetLine(1, "");
8475       progress->SetLine(2, "");
8476       progress->SetPercentage(0);
8477       progress->StartModal();
8478       progress->ShowProgressBar(true);
8479     }
8480
8481     int total = m_pDS->num_rows();
8482     int current = 0;
8483
8484     // create our xml document
8485     CXBMCTinyXML xmlDoc;
8486     TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8487     xmlDoc.InsertEndChild(decl);
8488     TiXmlNode *pMain = NULL;
8489     if (singleFiles)
8490       pMain = &xmlDoc;
8491     else
8492     {
8493       TiXmlElement xmlMainElement("videodb");
8494       pMain = xmlDoc.InsertEndChild(xmlMainElement);
8495       XMLUtils::SetInt(pMain,"version", GetExportVersion());
8496     }
8497
8498     while (!m_pDS->eof())
8499     {
8500       CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8501       // strip paths to make them relative
8502       if (movie.m_strTrailer.Mid(0,movie.m_strPath.size()).Equals(movie.m_strPath))
8503         movie.m_strTrailer = movie.m_strTrailer.Mid(movie.m_strPath.size());
8504       map<string, string> artwork;
8505       if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8506       {
8507         TiXmlElement additionalNode("art");
8508         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8509           XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8510         movie.Save(pMain, "movie", true, &additionalNode);
8511       }
8512       else
8513         movie.Save(pMain, "movie", !singleFiles);
8514
8515       // reset old skip state
8516       bool bSkip = false;
8517
8518       if (progress)
8519       {
8520         progress->SetLine(1, movie.m_strTitle);
8521         progress->SetPercentage(current * 100 / total);
8522         progress->Progress();
8523         if (progress->IsCanceled())
8524         {
8525           progress->Close();
8526           m_pDS->close();
8527           return;
8528         }
8529       }
8530
8531       CFileItem item(movie.m_strFileNameAndPath,false);
8532       if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8533       {
8534         if (!item.Exists(false))
8535         {
8536           CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8537           bSkip = true;
8538         }
8539         else
8540         {
8541           CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8542
8543           if (item.IsOpticalMediaFile())
8544           {
8545             nfoFile = URIUtils::AddFileToFolder(
8546                                     URIUtils::GetParentPath(nfoFile),
8547                                     URIUtils::GetFileName(nfoFile));
8548           }
8549
8550           if (overwrite || !CFile::Exists(nfoFile, false))
8551           {
8552             if(!xmlDoc.SaveFile(nfoFile))
8553             {
8554               CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8555               bSkip = ExportSkipEntry(nfoFile);
8556               if (!bSkip)
8557               {
8558                 if (progress)
8559                 {
8560                   progress->Close();
8561                   m_pDS->close();
8562                   return;
8563                 }
8564               }
8565             }
8566           }
8567         }
8568       }
8569       if (singleFiles)
8570       {
8571         xmlDoc.Clear();
8572         TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8573         xmlDoc.InsertEndChild(decl);
8574       }
8575
8576       if (images && !bSkip)
8577       {
8578         if (!singleFiles)
8579         {
8580           CStdString strFileName(movie.m_strTitle);
8581           if (movie.m_iYear > 0)
8582             strFileName.AppendFormat("_%i", movie.m_iYear);
8583           item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8584         }
8585         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8586         {
8587           CStdString savedThumb = item.GetLocalArt(i->first, false);
8588           CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8589         }
8590         if (actorThumbs)
8591           ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8592       }
8593       m_pDS->next();
8594       current++;
8595     }
8596     m_pDS->close();
8597
8598     // find all musicvideos
8599     sql = "select * from musicvideoview";
8600
8601     m_pDS->query(sql.c_str());
8602
8603     total = m_pDS->num_rows();
8604     current = 0;
8605
8606     while (!m_pDS->eof())
8607     {
8608       CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8609       map<string, string> artwork;
8610       if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8611       {
8612         TiXmlElement additionalNode("art");
8613         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8614           XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8615         movie.Save(pMain, "musicvideo", true, &additionalNode);
8616       }
8617       else
8618         movie.Save(pMain, "musicvideo", !singleFiles);
8619
8620       // reset old skip state
8621       bool bSkip = false;
8622
8623       if (progress)
8624       {
8625         progress->SetLine(1, movie.m_strTitle);
8626         progress->SetPercentage(current * 100 / total);
8627         progress->Progress();
8628         if (progress->IsCanceled())
8629         {
8630           progress->Close();
8631           m_pDS->close();
8632           return;
8633         }
8634       }
8635
8636       CFileItem item(movie.m_strFileNameAndPath,false);
8637       if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8638       {
8639         if (!item.Exists(false))
8640         {
8641           CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8642           bSkip = true;
8643         }
8644         else
8645         {
8646           CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8647
8648           if (overwrite || !CFile::Exists(nfoFile, false))
8649           {
8650             if(!xmlDoc.SaveFile(nfoFile))
8651             {
8652               CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8653               bSkip = ExportSkipEntry(nfoFile);
8654               if (!bSkip)
8655               {
8656                 if (progress)
8657                 {
8658                   progress->Close();
8659                   m_pDS->close();
8660                   return;
8661                 }
8662               }
8663             }
8664           }
8665         }
8666       }
8667       if (singleFiles)
8668       {
8669         xmlDoc.Clear();
8670         TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8671         xmlDoc.InsertEndChild(decl);
8672       }
8673       if (images && !bSkip)
8674       {
8675         if (!singleFiles)
8676         {
8677           CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8678           if (movie.m_iYear > 0)
8679             strFileName.AppendFormat("_%i", movie.m_iYear);
8680           item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8681         }
8682         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8683         {
8684           CStdString savedThumb = item.GetLocalArt(i->first, false);
8685           CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8686         }
8687       }
8688       m_pDS->next();
8689       current++;
8690     }
8691     m_pDS->close();
8692
8693     // repeat for all tvshows
8694     sql = "SELECT * FROM tvshowview";
8695     m_pDS->query(sql.c_str());
8696
8697     total = m_pDS->num_rows();
8698     current = 0;
8699
8700     while (!m_pDS->eof())
8701     {
8702       CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8703
8704       map<int, map<string, string> > seasonArt;
8705       GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8706
8707       map<string, string> artwork;
8708       if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8709       {
8710         TiXmlElement additionalNode("art");
8711         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8712           XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8713         for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8714         {
8715           TiXmlElement seasonNode("season");
8716           seasonNode.SetAttribute("num", i->first);
8717           for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8718             XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8719           additionalNode.InsertEndChild(seasonNode);
8720         }
8721         tvshow.Save(pMain, "tvshow", true, &additionalNode);
8722       }
8723       else
8724         tvshow.Save(pMain, "tvshow", !singleFiles);
8725
8726       // reset old skip state
8727       bool bSkip = false;
8728
8729       if (progress)
8730       {
8731         progress->SetLine(1, tvshow.m_strTitle);
8732         progress->SetPercentage(current * 100 / total);
8733         progress->Progress();
8734         if (progress->IsCanceled())
8735         {
8736           progress->Close();
8737           m_pDS->close();
8738           return;
8739         }
8740       }
8741
8742       // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8743       if (URIUtils::IsMultiPath(tvshow.m_strPath))
8744         tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8745
8746       CFileItem item(tvshow.m_strPath, true);
8747       if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8748       {
8749         if (!item.Exists(false))
8750         {
8751           CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8752           bSkip = true;
8753         }
8754         else
8755         {
8756           CStdString nfoFile = URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo");
8757
8758           if (overwrite || !CFile::Exists(nfoFile, false))
8759           {
8760             if(!xmlDoc.SaveFile(nfoFile))
8761             {
8762               CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8763               bSkip = ExportSkipEntry(nfoFile);
8764               if (!bSkip)
8765               {
8766                 if (progress)
8767                 {
8768                   progress->Close();
8769                   m_pDS->close();
8770                   return;
8771                 }
8772               }
8773             }
8774           }
8775         }
8776       }
8777       if (singleFiles)
8778       {
8779         xmlDoc.Clear();
8780         TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8781         xmlDoc.InsertEndChild(decl);
8782       }
8783       if (images && !bSkip)
8784       {
8785         if (!singleFiles)
8786           item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8787
8788         for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8789         {
8790           CStdString savedThumb = item.GetLocalArt(i->first, true);
8791           CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8792         }
8793
8794         if (actorThumbs)
8795           ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8796
8797         // export season thumbs
8798         for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8799         {
8800           string seasonThumb;
8801           if (i->first == -1)
8802             seasonThumb = "season-all";
8803           else if (i->first == 0)
8804             seasonThumb = "season-specials";
8805           else
8806             seasonThumb = StringUtils::Format("season%02i", i->first);
8807           for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8808           {
8809             CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8810             if (!i->second.empty())
8811               CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8812           }
8813         }
8814       }
8815
8816       // now save the episodes from this show
8817       sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8818       pDS->query(sql.c_str());
8819       CStdString showDir(item.GetPath());
8820
8821       while (!pDS->eof())
8822       {
8823         CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8824         map<string, string> artwork;
8825         if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8826         {
8827           TiXmlElement additionalNode("art");
8828           for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8829             XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8830           episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8831         }
8832         else if (!singleFiles)
8833           episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8834         else
8835           episode.Save(pMain, "episodedetails", !singleFiles);
8836         pDS->next();
8837         // multi-episode files need dumping to the same XML
8838         while (singleFiles && !pDS->eof() &&
8839                episode.m_iFileId == pDS->fv("idFile").get_asInt())
8840         {
8841           episode = GetDetailsForEpisode(pDS, true);
8842           episode.Save(pMain, "episodedetails", !singleFiles);
8843           pDS->next();
8844         }
8845
8846         // reset old skip state
8847         bool bSkip = false;
8848
8849         CFileItem item(episode.m_strFileNameAndPath, false);
8850         if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8851         {
8852           if (!item.Exists(false))
8853           {
8854             CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8855             bSkip = true;
8856           }
8857           else
8858           {
8859             CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8860
8861             if (overwrite || !CFile::Exists(nfoFile, false))
8862             {
8863               if(!xmlDoc.SaveFile(nfoFile))
8864               {
8865                 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8866                 bSkip = ExportSkipEntry(nfoFile);
8867                 if (!bSkip)
8868                 {
8869                   if (progress)
8870                   {
8871                     progress->Close();
8872                     m_pDS->close();
8873                     return;
8874                   }
8875                 }
8876               }
8877             }
8878           }
8879         }
8880         if (singleFiles)
8881         {
8882           xmlDoc.Clear();
8883           TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8884           xmlDoc.InsertEndChild(decl);
8885         }
8886
8887         if (images && !bSkip)
8888         {
8889           if (!singleFiles)
8890           {
8891             CStdString epName = StringUtils::Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8892             item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8893           }
8894           for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8895           {
8896             CStdString savedThumb = item.GetLocalArt(i->first, false);
8897             CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8898           }
8899           if (actorThumbs)
8900             ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8901         }
8902       }
8903       pDS->close();
8904       m_pDS->next();
8905       current++;
8906     }
8907     m_pDS->close();
8908
8909     if (singleFiles && progress)
8910     {
8911       progress->SetPercentage(100);
8912       progress->Progress();
8913     }
8914
8915     if (!singleFiles)
8916     {
8917       // now dump path info
8918       set<CStdString> paths;
8919       GetPaths(paths);
8920       TiXmlElement xmlPathElement("paths");
8921       TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8922       for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8923       {
8924         bool foundDirectly = false;
8925         SScanSettings settings;
8926         ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8927         if (info && foundDirectly)
8928         {
8929           TiXmlElement xmlPathElement2("path");
8930           TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8931           XMLUtils::SetString(pPath,"url", *iter);
8932           XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8933           XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8934           XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8935           XMLUtils::SetString(pPath,"scraperpath", info->ID());
8936         }
8937       }
8938       xmlDoc.SaveFile(xmlFile);
8939     }
8940   }
8941   catch (...)
8942   {
8943     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8944   }
8945
8946   if (progress)
8947     progress->Close();
8948 }
8949
8950 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8951 {
8952   CStdString strPath(strDir);
8953   if (singleFiles)
8954   {
8955     strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8956     if (!CDirectory::Exists(strPath))
8957     {
8958       CDirectory::Create(strPath);
8959       CFile::SetHidden(strPath, true);
8960     }
8961   }
8962
8963   for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8964   {
8965     CFileItem item;
8966     item.SetLabel(iter->strName);
8967     if (!iter->thumb.empty())
8968     {
8969       CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8970       CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8971     }
8972   }
8973 }
8974
8975 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8976 {
8977   CStdString strParent;
8978   URIUtils::GetParentPath(nfoFile,strParent);
8979   CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8980
8981   bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8982
8983   if (bSkip)
8984     CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8985   else
8986     CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8987
8988   return bSkip;
8989 }
8990
8991 void CVideoDatabase::ImportFromXML(const CStdString &path)
8992 {
8993   CGUIDialogProgress *progress=NULL;
8994   try
8995   {
8996     if (NULL == m_pDB.get()) return;
8997     if (NULL == m_pDS.get()) return;
8998
8999     CXBMCTinyXML xmlDoc;
9000     if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
9001       return;
9002
9003     TiXmlElement *root = xmlDoc.RootElement();
9004     if (!root) return;
9005
9006     progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
9007     if (progress)
9008     {
9009       progress->SetHeading(648);
9010       progress->SetLine(0, 649);
9011       progress->SetLine(1, 330);
9012       progress->SetLine(2, "");
9013       progress->SetPercentage(0);
9014       progress->StartModal();
9015       progress->ShowProgressBar(true);
9016     }
9017
9018     int iVersion = 0;
9019     XMLUtils::GetInt(root, "version", iVersion);
9020
9021     CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
9022     BeginTransaction();
9023
9024     TiXmlElement *movie = root->FirstChildElement();
9025     int current = 0;
9026     int total = 0;
9027     // first count the number of items...
9028     while (movie)
9029     {
9030       if (strnicmp(movie->Value(), "movie", 5)==0 ||
9031           strnicmp(movie->Value(), "tvshow", 6)==0 ||
9032           strnicmp(movie->Value(), "musicvideo",10)==0 )
9033         total++;
9034       movie = movie->NextSiblingElement();
9035     }
9036
9037     CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
9038     CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
9039     CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
9040     CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
9041     CVideoInfoScanner scanner;
9042     // add paths first (so we have scraper settings available)
9043     TiXmlElement *path = root->FirstChildElement("paths");
9044     path = path->FirstChildElement();
9045     while (path)
9046     {
9047       CStdString strPath;
9048       if (XMLUtils::GetString(path,"url",strPath) && !strPath.empty())
9049         AddPath(strPath);
9050
9051       CStdString content;
9052       if (XMLUtils::GetString(path,"content", content) && !content.empty())
9053       { // check the scraper exists, if so store the path
9054         AddonPtr addon;
9055         CStdString id;
9056         XMLUtils::GetString(path,"scraperpath",id);
9057         if (CAddonMgr::Get().GetAddon(id, addon))
9058         {
9059           SScanSettings settings;
9060           ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
9061           // FIXME: scraper settings are not exported?
9062           scraper->SetPathSettings(TranslateContent(content), "");
9063           XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
9064           XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
9065           SetScraperForPath(strPath,scraper,settings);
9066         }
9067       }
9068       path = path->NextSiblingElement();
9069     }
9070     movie = root->FirstChildElement();
9071     while (movie)
9072     {
9073       CVideoInfoTag info;
9074       if (strnicmp(movie->Value(), "movie", 5) == 0)
9075       {
9076         info.Load(movie);
9077         CFileItem item(info);
9078         bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
9079         CStdString filename = info.m_strTitle;
9080         if (info.m_iYear > 0)
9081           filename.AppendFormat("_%i", info.m_iYear);
9082         CFileItem artItem(item);
9083         artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
9084         scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9085         item.SetArt(artItem.GetArt());
9086         scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
9087         current++;
9088       }
9089       else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
9090       {
9091         info.Load(movie);
9092         CFileItem item(info);
9093         bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
9094         CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
9095         if (info.m_iYear > 0)
9096           filename.AppendFormat("_%i", info.m_iYear);
9097         CFileItem artItem(item);
9098         artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
9099         scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9100         item.SetArt(artItem.GetArt());
9101         scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
9102         current++;
9103       }
9104       else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
9105       {
9106         // load the TV show in.  NOTE: This deletes all episodes under the TV Show, which may not be
9107         // what we desire.  It may make better sense to only delete (or even better, update) the show information
9108         info.Load(movie);
9109         URIUtils::AddSlashAtEnd(info.m_strPath);
9110         DeleteTvShow(info.m_strPath);
9111         CFileItem showItem(info);
9112         bool useFolders = info.m_basePath.empty() ? LookupByFolders(showItem.GetPath(), true) : false;
9113         CFileItem artItem(showItem);
9114         CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
9115         artItem.SetPath(artPath);
9116         scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9117         showItem.SetArt(artItem.GetArt());
9118         int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
9119         // season artwork
9120         map<int, map<string, string> > seasonArt;
9121         artItem.GetVideoInfoTag()->m_strPath = artPath;
9122         scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
9123         for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
9124         {
9125           int seasonID = AddSeason(showID, i->first);
9126           SetArtForItem(seasonID, "season", i->second);
9127         }
9128         current++;
9129         // now load the episodes
9130         TiXmlElement *episode = movie->FirstChildElement("episodedetails");
9131         while (episode)
9132         {
9133           // no need to delete the episode info, due to the above deletion
9134           CVideoInfoTag info;
9135           info.Load(episode);
9136           CFileItem item(info);
9137           CStdString filename = StringUtils::Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
9138           CFileItem artItem(item);
9139           artItem.SetPath(GetSafeFile(artPath, filename));
9140           scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9141           item.SetArt(artItem.GetArt());
9142           scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
9143           episode = episode->NextSiblingElement("episodedetails");
9144         }
9145       }
9146       movie = movie->NextSiblingElement();
9147       if (progress && total)
9148       {
9149         progress->SetPercentage(current * 100 / total);
9150         progress->SetLine(2, info.m_strTitle);
9151         progress->Progress();
9152         if (progress->IsCanceled())
9153         {
9154           progress->Close();
9155           RollbackTransaction();
9156           return;
9157         }
9158       }
9159     }
9160
9161     CommitTransaction();
9162   }
9163   catch (...)
9164   {
9165     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
9166     RollbackTransaction();
9167   }
9168   if (progress)
9169     progress->Close();
9170 }
9171
9172 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
9173 {
9174   if (!node) return false;
9175   const TiXmlNode *art = node->FirstChild();
9176   while (art && art->FirstChild())
9177   {
9178     artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
9179     art = art->NextSibling();
9180   }
9181   return !artwork.empty();
9182 }
9183
9184 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
9185 {
9186   if (URIUtils::IsStack(strFileName) || 
9187       URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
9188     strDest = strFileName;
9189   else
9190     strDest = URIUtils::AddFileToFolder(strPath, strFileName);
9191 }
9192
9193 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
9194 {
9195   if (URIUtils::IsStack(strFileNameAndPath) || StringUtils::StartsWithNoCase(strFileNameAndPath, "rar://") || StringUtils::StartsWithNoCase(strFileNameAndPath, "zip://"))
9196   {
9197     URIUtils::GetParentPath(strFileNameAndPath,strPath);
9198     strFileName = strFileNameAndPath;
9199   }
9200   else if (URIUtils::IsPlugin(strFileNameAndPath))
9201   {
9202     CURL url(strFileNameAndPath);
9203     strPath = url.GetWithoutFilename();
9204     strFileName = strFileNameAndPath;
9205   }
9206   else
9207     URIUtils::Split(strFileNameAndPath,strPath, strFileName);
9208 }
9209
9210 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
9211 {
9212   SScanSettings settings;
9213   bool foundDirectly;
9214   ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
9215   SetPathHash(strPath,"");
9216   if (!info)
9217     return;
9218   if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
9219   {
9220     if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
9221     {
9222       CStdString strParent;
9223       URIUtils::GetParentPath(strPath,strParent);
9224       SetPathHash(strParent,"");
9225     }
9226   }
9227 }
9228
9229 bool CVideoDatabase::CommitTransaction()
9230 {
9231   if (CDatabase::CommitTransaction())
9232   { // number of items in the db has likely changed, so recalculate
9233     g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
9234     g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
9235     g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
9236     return true;
9237   }
9238   return false;
9239 }
9240
9241 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, int dbField, const std::string &strValue)
9242 {
9243   string strSQL;
9244   try
9245   {
9246     if (NULL == m_pDB.get() || NULL == m_pDS.get())
9247       return false;
9248
9249     string strTable, strField;
9250     if (type == VIDEODB_CONTENT_MOVIES)
9251     {
9252       strTable = "movie";
9253       strField = "idMovie";
9254     }
9255     else if (type == VIDEODB_CONTENT_TVSHOWS)
9256     {
9257       strTable = "tvshow";
9258       strField = "idShow";
9259     }
9260     else if (type == VIDEODB_CONTENT_EPISODES)
9261     {
9262       strTable = "episode";
9263       strField = "idEpisode";
9264     }
9265     else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
9266     {
9267       strTable = "musicvideo";
9268       strField = "idMVideo";
9269     }
9270
9271     if (strTable.empty())
9272       return false;
9273
9274     return SetSingleValue(strTable, StringUtils::Format("c%02u", dbField), strValue, strField, dbId);
9275   }
9276   catch (...)
9277   {
9278     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
9279   }
9280   return false;
9281 }
9282
9283 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, Field dbField, const std::string &strValue)
9284 {
9285   MediaType mediaType = DatabaseUtils::MediaTypeFromVideoContentType(type);
9286   if (mediaType == MediaTypeNone)
9287     return false;
9288
9289   int dbFieldIndex = DatabaseUtils::GetField(dbField, mediaType);
9290   if (dbFieldIndex < 0)
9291     return false;
9292
9293   return SetSingleValue(type, dbId, dbFieldIndex, strValue);
9294 }
9295
9296 bool CVideoDatabase::SetSingleValue(const std::string &table, const std::string &fieldName, const std::string &strValue,
9297                                     const std::string &conditionName /* = "" */, int conditionValue /* = -1 */)
9298 {
9299   if (table.empty() || fieldName.empty())
9300     return false;
9301
9302   std::string sql;
9303   try
9304   {
9305     if (NULL == m_pDB.get() || NULL == m_pDS.get())
9306       return false;
9307
9308     sql = PrepareSQL("UPDATE %s SET %s='%s'", table.c_str(), fieldName.c_str(), strValue.c_str());
9309     if (!conditionName.empty())
9310       sql += PrepareSQL(" WHERE %s=%u", conditionName.c_str(), conditionValue);
9311     if (m_pDS->exec(sql.c_str()) == 0)
9312       return true;
9313   }
9314   catch (...)
9315   {
9316     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, sql.c_str());
9317   }
9318   return false;
9319 }
9320
9321 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
9322 {
9323   CStdString safeThumb(name);
9324   safeThumb.Replace(' ', '_');
9325   return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
9326 }
9327
9328 void CVideoDatabase::AnnounceRemove(std::string content, int id)
9329 {
9330   CVariant data;
9331   data["type"] = content;
9332   data["id"] = id;
9333   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9334 }
9335
9336 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9337 {
9338   CVariant data;
9339   data["type"] = content;
9340   data["id"] = id;
9341   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9342 }
9343
9344 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9345 {
9346   CStdString path(strPath);
9347   
9348   if(URIUtils::IsMultiPath(path))
9349   {
9350     vector<CStdString> paths;
9351     CMultiPathDirectory::GetPaths(path, paths);
9352
9353     for(unsigned i=0;i<paths.size();i++)
9354       GetItemsForPath(content, paths[i], items);
9355
9356     return items.Size() > 0;
9357   }
9358   
9359   int pathID = GetPathId(path);
9360   if (pathID < 0)
9361     return false;
9362
9363   if (content == "movies")
9364   {
9365     Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9366     GetMoviesByWhere("videodb://movies/titles/", filter, items);
9367   }
9368   else if (content == "episodes")
9369   {
9370     Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9371     GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
9372   }
9373   else if (content == "tvshows")
9374   {
9375     Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9376     GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
9377   }
9378   else if (content == "musicvideos")
9379   {
9380     Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9381     GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
9382   }
9383   for (int i = 0; i < items.Size(); i++)
9384     items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9385   return items.Size() > 0;
9386 }
9387
9388 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9389 {
9390   if (!videoUrl.IsValid())
9391     return false;
9392
9393   std::string type = videoUrl.GetType();
9394   std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9395   const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9396   CUrlOptions::UrlOptions::const_iterator option;
9397
9398   if (type == "movies")
9399   {
9400     option = options.find("genreid");
9401     if (option != options.end())
9402     {
9403       filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9404       filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9405     }
9406
9407     option = options.find("genre");
9408     if (option != options.end())
9409     {
9410       filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9411       filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9412     }
9413
9414     option = options.find("countryid");
9415     if (option != options.end())
9416     {
9417       filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9418       filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9419     }
9420
9421     option = options.find("country");
9422     if (option != options.end())
9423     {
9424       filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9425       filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9426     }
9427
9428     option = options.find("studioid");
9429     if (option != options.end())
9430     {
9431       filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9432       filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9433     }
9434
9435     option = options.find("studio");
9436     if (option != options.end())
9437     {
9438       filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9439       filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9440     }
9441
9442     option = options.find("directorid");
9443     if (option != options.end())
9444     {
9445       filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9446       filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9447     }
9448
9449     option = options.find("director");
9450     if (option != options.end())
9451     {
9452       filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9453       filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9454     }
9455
9456     option = options.find("year");
9457     if (option != options.end())
9458       filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9459
9460     option = options.find("actorid");
9461     if (option != options.end())
9462     {
9463       filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9464       filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9465     }
9466
9467     option = options.find("actor");
9468     if (option != options.end())
9469     {
9470       filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9471       filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9472     }
9473
9474     option = options.find("setid");
9475     if (option != options.end())
9476       filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9477
9478     option = options.find("set");
9479     if (option != options.end())
9480     {
9481       filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9482       filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9483     }
9484
9485     option = options.find("tagid");
9486     if (option != options.end())
9487     {
9488       filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9489       filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9490     }
9491
9492     option = options.find("tag");
9493     if (option != options.end())
9494     {
9495       filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9496       filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9497     }
9498   }
9499   else if (type == "tvshows")
9500   {
9501     if (itemType == "tvshows")
9502     {
9503       option = options.find("genreid");
9504       if (option != options.end())
9505       {
9506         filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9507         filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9508       }
9509
9510       option = options.find("genre");
9511       if (option != options.end())
9512       {
9513         filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9514         filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9515       }
9516
9517       option = options.find("studioid");
9518       if (option != options.end())
9519       {
9520         filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9521         filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9522       }
9523
9524       option = options.find("studio");
9525       if (option != options.end())
9526       {
9527         filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9528         filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9529       }
9530
9531       option = options.find("directorid");
9532       if (option != options.end())
9533       {
9534         filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9535         filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9536       }
9537
9538       option = options.find("year");
9539       if (option != options.end())
9540         filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9541
9542       option = options.find("actorid");
9543       if (option != options.end())
9544       {
9545         filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9546         filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9547       }
9548
9549       option = options.find("actor");
9550       if (option != options.end())
9551       {
9552         filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9553         filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9554       }
9555
9556       option = options.find("tagid");
9557       if (option != options.end())
9558       {
9559         filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9560         filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9561       }
9562
9563       option = options.find("tag");
9564       if (option != options.end())
9565       {
9566         filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9567         filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9568       }
9569     }
9570     else if (itemType == "seasons")
9571     {
9572       option = options.find("genreid");
9573       if (option != options.end())
9574       {
9575         filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9576         filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9577       }
9578
9579       option = options.find("directorid");
9580       if (option != options.end())
9581       {
9582         filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9583         filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9584       }
9585       
9586       option = options.find("year");
9587       if (option != options.end())
9588         filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9589
9590       option = options.find("actorid");
9591       if (option != options.end())
9592       {
9593         filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9594         filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9595       }
9596     }
9597     else if (itemType == "episodes")
9598     {
9599       int idShow = -1;
9600       option = options.find("tvshowid");
9601       if (option != options.end())
9602         idShow = (int)option->second.asInteger();
9603
9604       int season = -1;
9605       option = options.find("season");
9606       if (option != options.end())
9607         season = (int)option->second.asInteger();
9608
9609       CStdString strIn = PrepareSQL("= %i", idShow);
9610       GetStackedTvShowList(idShow, strIn);
9611
9612       if (idShow > -1)
9613       {
9614         bool condition = false;
9615
9616         option = options.find("genreid");
9617         if (option != options.end())
9618         {
9619           condition = true;
9620           filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9621           filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9622         }
9623
9624         option = options.find("genre");
9625         if (option != options.end())
9626         {
9627           condition = true;
9628           filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9629           filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9630         }
9631
9632         option = options.find("directorid");
9633         if (option != options.end())
9634         {
9635           condition = true;
9636           filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9637           filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9638         }
9639
9640         option = options.find("director");
9641         if (option != options.end())
9642         {
9643           condition = true;
9644           filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9645           filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9646         }
9647       
9648         option = options.find("year");
9649         if (option != options.end())
9650         {
9651           condition = true;
9652           filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9653         }
9654
9655         option = options.find("actorid");
9656         if (option != options.end())
9657         {
9658           condition = true;
9659           filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9660           filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9661         }
9662
9663         option = options.find("actor");
9664         if (option != options.end())
9665         {
9666           condition = true;
9667           filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9668           filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9669         }
9670
9671         if (!condition)
9672           filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9673
9674         if (season > -1)
9675         {
9676           if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9677             filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9678           else
9679             filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9680               VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9681         }
9682       }
9683       else
9684       {
9685         option = options.find("year");
9686         if (option != options.end())
9687           filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9688
9689         option = options.find("directorid");
9690         if (option != options.end())
9691         {
9692           filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9693           filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9694         }
9695
9696         option = options.find("director");
9697         if (option != options.end())
9698         {
9699           filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9700           filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9701         }
9702       }
9703     }
9704   }
9705   else if (type == "musicvideos")
9706   {
9707     option = options.find("genreid");
9708     if (option != options.end())
9709     {
9710       filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9711       filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9712     }
9713
9714     option = options.find("genre");
9715     if (option != options.end())
9716     {
9717       filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9718       filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9719     }
9720
9721     option = options.find("studioid");
9722     if (option != options.end())
9723     {
9724       filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9725       filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9726     }
9727
9728     option = options.find("studio");
9729     if (option != options.end())
9730     {
9731       filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9732       filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9733     }
9734
9735     option = options.find("directorid");
9736     if (option != options.end())
9737     {
9738       filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9739       filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9740     }
9741
9742     option = options.find("director");
9743     if (option != options.end())
9744     {
9745       filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9746       filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9747     }
9748
9749     option = options.find("year");
9750     if (option != options.end())
9751       filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9752
9753     option = options.find("artistid");
9754     if (option != options.end())
9755     {
9756       filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9757       filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9758     }
9759
9760     option = options.find("artist");
9761     if (option != options.end())
9762     {
9763       filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9764       filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9765     }
9766
9767     option = options.find("albumid");
9768     if (option != options.end())
9769       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()));
9770
9771     option = options.find("tagid");
9772     if (option != options.end())
9773     {
9774       filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9775       filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9776     }
9777
9778     option = options.find("tag");
9779     if (option != options.end())
9780     {
9781       filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9782       filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9783     }
9784   }
9785   else
9786     return false;
9787
9788   option = options.find("xsp");
9789   if (option != options.end())
9790   {
9791     CSmartPlaylist xsp;
9792     if (!xsp.LoadFromJson(option->second.asString()))
9793       return false;
9794
9795     // check if the filter playlist matches the item type
9796     if (xsp.GetType() == itemType ||
9797        (xsp.GetGroup() == itemType && !xsp.IsGroupMixed()) ||
9798         // handle episode listings with videodb://tvshows/titles/ which get the rest
9799         // of the path (season and episodeid) appended later
9800        (xsp.GetType() == "episodes" && itemType == "tvshows"))
9801     {
9802       std::set<CStdString> playlists;
9803       filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9804
9805       if (xsp.GetLimit() > 0)
9806         sorting.limitEnd = xsp.GetLimit();
9807       if (xsp.GetOrder() != SortByNone)
9808         sorting.sortBy = xsp.GetOrder();
9809       if (xsp.GetOrderDirection() != SortOrderNone)
9810         sorting.sortOrder = xsp.GetOrderDirection();
9811       if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
9812         sorting.sortAttributes = SortAttributeIgnoreArticle;
9813     }
9814   }
9815
9816   option = options.find("filter");
9817   if (option != options.end())
9818   {
9819     CSmartPlaylist xspFilter;
9820     if (!xspFilter.LoadFromJson(option->second.asString()))
9821       return false;
9822
9823     // check if the filter playlist matches the item type
9824     if (xspFilter.GetType() == itemType)
9825     {
9826       std::set<CStdString> playlists;
9827       filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9828     }
9829     // remove the filter if it doesn't match the item type
9830     else
9831       videoUrl.RemoveOption("filter");
9832   }
9833
9834   return true;
9835 }