Merge remote branch 'mine/ext-python'
[vuplus_xbmc] / xbmc / video / VideoDatabase.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
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/GUIDialogProgress.h"
39 #include "dialogs/GUIDialogYesNo.h"
40 #include "FileItem.h"
41 #include "settings/AdvancedSettings.h"
42 #include "settings/GUISettings.h"
43 #include "settings/Settings.h"
44 #include "utils/StringUtils.h"
45 #include "guilib/LocalizeStrings.h"
46 #include "utils/TimeUtils.h"
47 #include "utils/log.h"
48 #include "TextureCache.h"
49 #include "addons/AddonInstaller.h"
50 #include "interfaces/AnnouncementManager.h"
51 #include "dbwrappers/dataset.h"
52
53 using namespace std;
54 using namespace dbiplus;
55 using namespace XFILE;
56 using namespace VIDEO;
57 using namespace ADDON;
58
59 //********************************************************************************************************************************
60 CVideoDatabase::CVideoDatabase(void)
61 {
62 }
63
64 //********************************************************************************************************************************
65 CVideoDatabase::~CVideoDatabase(void)
66 {}
67
68 //********************************************************************************************************************************
69 bool CVideoDatabase::Open()
70 {
71   return CDatabase::Open(g_advancedSettings.m_databaseVideo);
72 }
73
74 bool CVideoDatabase::CreateTables()
75 {
76   /* indexes should be added on any columns that are used in in  */
77   /* a where or a join. primary key on a column is the same as a */
78   /* unique index on that column, so there is no need to add any */
79   /* index if no other columns are refered                       */
80
81   /* order of indexes are important, for an index to be considered all  */
82   /* columns up to the column in question have to have been specified   */
83   /* select * from actorlinkmovie where idMovie = 1, can not take       */
84   /* advantage of a index that has been created on ( idGenre, idMovie ) */
85   /*, hower on on ( idMovie, idGenre ) will be considered for use       */
86
87   BeginTransaction();
88   try
89   {
90     CDatabase::CreateTables();
91
92     CLog::Log(LOGINFO, "create bookmark table");
93     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");
94     m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile)");
95
96     CLog::Log(LOGINFO, "create settings table");
97     m_pDS->exec("CREATE TABLE settings ( idFile integer, Deinterlace bool,"
98                 "ViewMode integer,ZoomAmount float, PixelRatio float, VerticalShift float, AudioStream integer, SubtitleStream integer,"
99                 "SubtitleDelay float, SubtitlesOn bool, Brightness float, Contrast float, Gamma float,"
100                 "VolumeAmplification float, AudioDelay float, OutputToAllSpeakers bool, ResumeTime integer, Crop bool, CropLeft integer,"
101                 "CropRight integer, CropTop integer, CropBottom integer, Sharpness float, NoiseReduction float, NonLinStretch bool, PostProcess bool, ScalingMethod integer)\n");
102     m_pDS->exec("CREATE UNIQUE INDEX ix_settings ON settings ( idFile )\n");
103
104     CLog::Log(LOGINFO, "create stacktimes table");
105     m_pDS->exec("CREATE TABLE stacktimes (idFile integer, times text)\n");
106     m_pDS->exec("CREATE UNIQUE INDEX ix_stacktimes ON stacktimes ( idFile )\n");
107
108     CLog::Log(LOGINFO, "create genre table");
109     m_pDS->exec("CREATE TABLE genre ( idGenre integer primary key, strGenre text)\n");
110
111     CLog::Log(LOGINFO, "create genrelinkmovie table");
112     m_pDS->exec("CREATE TABLE genrelinkmovie ( idGenre integer, idMovie integer)\n");
113     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_1 ON genrelinkmovie ( idGenre, idMovie)\n");
114     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_2 ON genrelinkmovie ( idMovie, idGenre)\n");
115
116     CLog::Log(LOGINFO, "create country table");
117     m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
118
119     CLog::Log(LOGINFO, "create countrylinkmovie table");
120     m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
121     m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
122     m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
123
124     CLog::Log(LOGINFO, "create movie table");
125     CStdString columns = "CREATE TABLE movie ( idMovie integer primary key, idFile integer";
126     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
127     {
128       CStdString column;
129       column.Format(",c%02d text", i);
130       columns += column;
131     }
132     columns += ")";
133     m_pDS->exec(columns.c_str());
134     m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
135     m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
136
137     CLog::Log(LOGINFO, "create actorlinkmovie table");
138     m_pDS->exec("CREATE TABLE actorlinkmovie ( idActor integer, idMovie integer, strRole text, iOrder integer)\n");
139     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_1 ON actorlinkmovie ( idActor, idMovie )\n");
140     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_2 ON actorlinkmovie ( idMovie, idActor )\n");
141
142     CLog::Log(LOGINFO, "create directorlinkmovie table");
143     m_pDS->exec("CREATE TABLE directorlinkmovie ( idDirector integer, idMovie integer)\n");
144     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_1 ON directorlinkmovie ( idDirector, idMovie )\n");
145     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_2 ON directorlinkmovie ( idMovie, idDirector )\n");
146
147     CLog::Log(LOGINFO, "create writerlinkmovie table");
148     m_pDS->exec("CREATE TABLE writerlinkmovie ( idWriter integer, idMovie integer)\n");
149     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_1 ON writerlinkmovie ( idWriter, idMovie )\n");
150     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_2 ON writerlinkmovie ( idMovie, idWriter )\n");
151
152     CLog::Log(LOGINFO, "create actors table");
153     m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
154
155     CLog::Log(LOGINFO, "create path table");
156     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)");
157     m_pDS->exec("CREATE UNIQUE INDEX ix_path ON path ( strPath(255) )");
158
159     CLog::Log(LOGINFO, "create files table");
160     m_pDS->exec("CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text)");
161     m_pDS->exec("CREATE UNIQUE INDEX ix_files ON files ( idPath, strFilename(255) )");
162
163     CLog::Log(LOGINFO, "create tvshow table");
164     columns = "CREATE TABLE tvshow ( idShow integer primary key";
165     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
166     {
167       CStdString column;
168       column.Format(",c%02d text", i);
169       columns += column;
170     }
171     columns += ")";
172     m_pDS->exec(columns.c_str());
173
174     CLog::Log(LOGINFO, "create directorlinktvshow table");
175     m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
176     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
177     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
178
179     CLog::Log(LOGINFO, "create actorlinktvshow table");
180     m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text, iOrder integer)\n");
181     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
182     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
183
184     CLog::Log(LOGINFO, "create studiolinktvshow table");
185     m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
186     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
187     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
188
189     CLog::Log(LOGINFO, "create episode table");
190     columns = "CREATE TABLE episode ( idEpisode integer primary key, idFile integer";
191     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
192     {
193       CStdString column;
194       if ( i == VIDEODB_ID_EPISODE_SEASON || i == VIDEODB_ID_EPISODE_EPISODE || i == VIDEODB_ID_EPISODE_BOOKMARK)
195         column.Format(",c%02d varchar(24)", i);
196       else
197         column.Format(",c%02d text", i);
198
199       columns += column;
200     }
201     columns += ")";
202     m_pDS->exec(columns.c_str());
203     m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
204     m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
205     CStdString createColIndex;
206     createColIndex.Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
207     m_pDS->exec(createColIndex.c_str());
208     createColIndex.Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
209     m_pDS->exec(createColIndex.c_str());
210
211     CLog::Log(LOGINFO, "create tvshowlinkepisode table");
212     m_pDS->exec("CREATE TABLE tvshowlinkepisode ( idShow integer, idEpisode integer)\n");
213     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkepisode_1 ON tvshowlinkepisode ( idShow, idEpisode )\n");
214     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkepisode_2 ON tvshowlinkepisode ( idEpisode, idShow )\n");
215
216     CLog::Log(LOGINFO, "create tvshowlinkpath table");
217     m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
218     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
219     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
220
221     CLog::Log(LOGINFO, "create actorlinkepisode table");
222     m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text, iOrder integer)\n");
223     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
224     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
225
226     CLog::Log(LOGINFO, "create directorlinkepisode table");
227     m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
228     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
229     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
230
231     CLog::Log(LOGINFO, "create writerlinkepisode table");
232     m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
233     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
234     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
235
236     CLog::Log(LOGINFO, "create genrelinktvshow table");
237     m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
238     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
239     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
240
241     CLog::Log(LOGINFO, "create movielinktvshow table");
242     m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
243     m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
244     m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
245
246     CLog::Log(LOGINFO, "create studio table");
247     m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
248
249     CLog::Log(LOGINFO, "create studiolinkmovie table");
250     m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
251     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
252     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
253
254     CLog::Log(LOGINFO, "create musicvideo table");
255     columns = "CREATE TABLE musicvideo ( idMVideo integer primary key, idFile integer";
256     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
257     {
258       CStdString column;
259       column.Format(",c%02d text", i);
260       columns += column;
261     }
262     columns += ")";
263     m_pDS->exec(columns.c_str());
264
265     // create views
266     CreateViews();
267
268     m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
269     m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
270
271     CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
272     m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
273     m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
274     m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
275
276     CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
277     m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
278     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
279     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
280
281     CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
282     m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
283     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
284     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
285
286     CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
287     m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
288     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
289     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
290
291     CLog::Log(LOGINFO, "create streaminfo table");
292     m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
293       "strVideoCodec text, fVideoAspect float, iVideoWidth integer, iVideoHeight integer, "
294       "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text, iVideoDuration integer)");
295     m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
296
297    CLog::Log(LOGINFO, "create sets table");
298     m_pDS->exec("CREATE TABLE sets ( idSet integer primary key, strSet text)\n");
299
300     CLog::Log(LOGINFO, "create setlinkmovie table");
301     m_pDS->exec("CREATE TABLE setlinkmovie ( idSet integer, idMovie integer)\n");
302     m_pDS->exec("CREATE UNIQUE INDEX ix_setlinkmovie_1 ON setlinkmovie ( idSet, idMovie)\n");
303     m_pDS->exec("CREATE UNIQUE INDEX ix_setlinkmovie_2 ON setlinkmovie ( idMovie, idSet)\n");
304
305     // create basepath indices
306     m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c22(255) )");
307     m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c13(255) )");
308     m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c18(255) )");
309     m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c16(255) )");
310   }
311   catch (...)
312   {
313     CLog::Log(LOGERROR, "%s unable to create tables:%i", __FUNCTION__, (int)GetLastError());
314     RollbackTransaction();
315     return false;
316   }
317   CommitTransaction();
318   return true;
319 }
320
321 void CVideoDatabase::CreateViews()
322 {
323   CLog::Log(LOGINFO, "create episodeview");
324   m_pDS->exec("DROP VIEW IF EXISTS episodeview");
325   CStdString episodeview = PrepareSQL("create view episodeview as select episode.*,files.strFileName as strFileName,"
326                                       "path.strPath as strPath,files.playCount as playCount,files.lastPlayed as lastPlayed,tvshow.c%02d as strTitle,tvshow.c%02d as strStudio,tvshow.idShow as idShow,"
327                                       "tvshow.c%02d as premiered, tvshow.c%02d as mpaa from episode "
328                                       "join files on files.idFile=episode.idFile "
329                                       "join tvshowlinkepisode on episode.idEpisode=tvshowlinkepisode.idEpisode "
330                                       "join tvshow on tvshow.idShow=tvshowlinkepisode.idShow "
331                                       "join path on files.idPath=path.idPath",VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_PREMIERED, VIDEODB_ID_TV_MPAA);
332   m_pDS->exec(episodeview.c_str());
333
334   CLog::Log(LOGINFO, "create tvshowview");
335   m_pDS->exec("DROP VIEW IF EXISTS tvshowview");
336   CStdString tvshowview = PrepareSQL("CREATE VIEW tvshowview AS SELECT "
337                                      "tvshow.*,"
338                                      "path.strPath AS strPath,"
339                                      "  NULLIF(COUNT(episode.c12), 0) AS totalCount,"
340                                      "  COUNT(files.playCount) AS watchedcount,"
341                                      "  NULLIF(COUNT(DISTINCT(episode.c12)), 0) AS totalSeasons "
342                                      "FROM tvshow"
343                                      "  LEFT JOIN tvshowlinkpath ON"
344                                      "    tvshowlinkpath.idShow=tvshow.idShow"
345                                      "  LEFT JOIN path ON"
346                                      "    path.idPath=tvshowlinkpath.idPath"
347                                      "  LEFT JOIN tvshowlinkepisode ON"
348                                      "    tvshowlinkepisode.idShow=tvshow.idShow"
349                                      "  LEFT JOIN episode ON"
350                                      "    episode.idEpisode=tvshowlinkepisode.idEpisode"
351                                      "  LEFT JOIN files ON"
352                                      "    files.idFile=episode.idFile "
353                                      "GROUP BY tvshow.idShow;");
354   m_pDS->exec(tvshowview.c_str());
355
356   CLog::Log(LOGINFO, "create musicvideoview");
357   m_pDS->exec("DROP VIEW IF EXISTS musicvideoview");
358   m_pDS->exec("create view musicvideoview as select musicvideo.*,files.strFileName as strFileName,path.strPath as strPath,files.playCount as playCount,files.lastPlayed as lastPlayed "
359               "from musicvideo join files on files.idFile=musicvideo.idFile join path on path.idPath=files.idPath");
360
361   CLog::Log(LOGINFO, "create movieview");
362   m_pDS->exec("DROP VIEW IF EXISTS movieview");
363   m_pDS->exec("create view movieview as select movie.*,files.strFileName as strFileName,path.strPath as strPath,files.playCount as playCount,files.lastPlayed as lastPlayed "
364               "from movie join files on files.idFile=movie.idFile join path on path.idPath=files.idPath");
365 }
366
367 //********************************************************************************************************************************
368 int CVideoDatabase::GetPathId(const CStdString& strPath)
369 {
370   CStdString strSQL;
371   try
372   {
373     int idPath=-1;
374     if (NULL == m_pDB.get()) return -1;
375     if (NULL == m_pDS.get()) return -1;
376
377     CStdString strPath1(strPath);
378     if (URIUtils::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
379       URIUtils::GetParentPath(strPath,strPath1);
380
381     URIUtils::AddSlashAtEnd(strPath1);
382
383     strSQL=PrepareSQL("select idPath from path where strPath like '%s'",strPath1.c_str());
384     m_pDS->query(strSQL.c_str());
385     if (!m_pDS->eof())
386       idPath = m_pDS->fv("path.idPath").get_asInt();
387
388     m_pDS->close();
389     return idPath;
390   }
391   catch (...)
392   {
393     CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
394   }
395   return -1;
396 }
397
398 bool CVideoDatabase::GetPaths(set<CStdString> &paths)
399 {
400   try
401   {
402     if (NULL == m_pDB.get()) return false;
403     if (NULL == m_pDS.get()) return false;
404
405     paths.clear();
406
407     // grab all paths with movie content set
408     if (!m_pDS->query("select strPath,noUpdate from path"
409                       " where (strContent = 'movies' or strContent = 'musicvideos')"
410                       " and strPath NOT like 'multipath://%%'"
411                       " order by strPath"))
412       return false;
413
414     while (!m_pDS->eof())
415     {
416       if (!m_pDS->fv("noUpdate").get_asBool())
417         paths.insert(m_pDS->fv("strPath").get_asString());
418       m_pDS->next();
419     }
420     m_pDS->close();
421
422     // then grab all tvshow paths
423     if (!m_pDS->query("select strPath,noUpdate from path"
424                       " where ( strContent = 'tvshows'"
425                       "       or idPath in (select idPath from tvshowlinkpath))"
426                       " and strPath NOT like 'multipath://%%'"
427                       " order by strPath"))
428       return false;
429
430     while (!m_pDS->eof())
431     {
432       if (!m_pDS->fv("noUpdate").get_asBool())
433         paths.insert(m_pDS->fv("strPath").get_asString());
434       m_pDS->next();
435     }
436     m_pDS->close();
437
438     // finally grab all other paths holding a movie which is not a stack or a rar archive
439     // - this isnt perfect but it should do fine in most situations.
440     // reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
441     // not making mistakes must take priority
442     if (!m_pDS->query("select strPath,noUpdate from path"
443                        " where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
444                        " and idPath NOT in (select idPath from tvshowlinkpath)"
445                        " 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
446                        " and idPath NOT in (select idPath from files where strFileName like 'index.bdmv')" // bluray folders get stacked to a single item in parent folder
447                        " and strPath NOT like 'multipath://%%'"
448                        " and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
449                        " order by strPath"))
450
451       return false;
452     while (!m_pDS->eof())
453     {
454       if (!m_pDS->fv("noUpdate").get_asBool())
455         paths.insert(m_pDS->fv("strPath").get_asString());
456       m_pDS->next();
457     }
458     m_pDS->close();
459     return true;
460   }
461   catch (...)
462   {
463     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
464   }
465   return false;
466 }
467
468 bool CVideoDatabase::GetPathsForTvShow(int idShow, vector<int>& paths)
469 {
470   CStdString strSQL;
471   try
472   {
473     if (NULL == m_pDB.get()) return false;
474     if (NULL == m_pDS.get()) return false;
475     strSQL = PrepareSQL("SELECT DISTINCT idPath FROM files JOIN episode ON episode.idFile=files.idFile JOIN tvshowlinkepisode ON tvshowlinkepisode.idEpisode=episode.idEpisode WHERE tvshowlinkepisode.idShow=%i",idShow);
476     m_pDS->query(strSQL.c_str());
477     while (!m_pDS->eof())
478     {
479       paths.push_back(m_pDS->fv(0).get_asInt());
480       m_pDS->next();
481     }
482     m_pDS->close();
483     return true;
484   }
485   catch (...)
486   {
487     CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
488   }
489   return false;
490 }
491
492 int CVideoDatabase::RunQuery(const CStdString &sql)
493 {
494   unsigned int time = CTimeUtils::GetTimeMS();
495   int rows = -1;
496   if (m_pDS->query(sql.c_str()))
497   {
498     rows = m_pDS->num_rows();
499     if (rows == 0)
500       m_pDS->close();
501   }
502   CLog::Log(LOGDEBUG, "%s took %d ms for %d items query: %s", __FUNCTION__, CTimeUtils::GetTimeMS() - time, rows, sql.c_str());
503   return rows;
504 }
505
506 bool CVideoDatabase::GetSubPaths(const CStdString &basepath, vector<int>& subpaths)
507 {
508   CStdString sql;
509   try
510   {
511     if (!m_pDB.get() || !m_pDS.get())
512       return false;
513
514     sql = PrepareSQL("SELECT idPath FROM path WHERE strPath LIKE '%s%%'", basepath.c_str());
515     m_pDS->query(sql.c_str());
516     while (!m_pDS->eof())
517     {
518       subpaths.push_back(m_pDS->fv(0).get_asInt());
519       m_pDS->next();
520     }
521     m_pDS->close();
522     return true;
523   }
524   catch (...)
525   {
526     CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, sql.c_str());
527   }
528   return false;
529 }
530
531 int CVideoDatabase::AddPath(const CStdString& strPath)
532 {
533   CStdString strSQL;
534   try
535   {
536     int idPath;
537     if (NULL == m_pDB.get()) return -1;
538     if (NULL == m_pDS.get()) return -1;
539
540     CStdString strPath1(strPath);
541     if (URIUtils::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
542       URIUtils::GetParentPath(strPath,strPath1);
543
544     URIUtils::AddSlashAtEnd(strPath1);
545
546     strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
547     m_pDS->exec(strSQL.c_str());
548     idPath = (int)m_pDS->lastinsertid();
549     return idPath;
550   }
551   catch (...)
552   {
553     CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
554   }
555   return -1;
556 }
557
558 bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
559 {
560   try
561   {
562     if (NULL == m_pDB.get()) return false;
563     if (NULL == m_pDS.get()) return false;
564
565     CStdString strSQL=PrepareSQL("select strHash from path where strPath like '%s'", path.c_str());
566     m_pDS->query(strSQL.c_str());
567     if (m_pDS->num_rows() == 0)
568       return false;
569     hash = m_pDS->fv("strHash").get_asString();
570     return true;
571   }
572   catch (...)
573   {
574     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
575   }
576
577   return false;
578 }
579
580 //********************************************************************************************************************************
581 int CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
582 {
583   CStdString strSQL = "";
584   try
585   {
586     int idFile;
587     if (NULL == m_pDB.get()) return -1;
588     if (NULL == m_pDS.get()) return -1;
589
590     CStdString strFileName, strPath;
591     SplitPath(strFileNameAndPath,strPath,strFileName);
592
593     int idPath=GetPathId(strPath);
594     if (idPath < 0)
595       idPath = AddPath(strPath);
596
597     if (idPath < 0)
598       return -1;
599
600     CStdString strSQL=PrepareSQL("select idFile from files where strFileName like '%s' and idPath=%i", strFileName.c_str(),idPath);
601
602     m_pDS->query(strSQL.c_str());
603     if (m_pDS->num_rows() > 0)
604     {
605       idFile = m_pDS->fv("idFile").get_asInt() ;
606       m_pDS->close();
607       return idFile;
608     }
609     m_pDS->close();
610     strSQL=PrepareSQL("insert into files (idFile,idPath,strFileName) values(NULL, %i, '%s')", idPath,strFileName.c_str());
611     m_pDS->exec(strSQL.c_str());
612     idFile = (int)m_pDS->lastinsertid();
613     return idFile;
614   }
615   catch (...)
616   {
617     CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
618   }
619   return -1;
620 }
621
622 int CVideoDatabase::AddFile(const CFileItem& item)
623 {
624   if (item.IsVideoDb() && item.HasVideoInfoTag())
625     return AddFile(item.GetVideoInfoTag()->m_strFileNameAndPath);
626   return AddFile(item.m_strPath);
627 }
628
629 bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
630 {
631   try
632   {
633     if (NULL == m_pDB.get()) return false;
634     if (NULL == m_pDS.get()) return false;
635
636     if (hash.IsEmpty())
637     { // this is an empty folder - we need only add it to the path table
638       // if the path actually exists
639       if (!CDirectory::Exists(path))
640         return false;
641     }
642     int idPath = GetPathId(path);
643     if (idPath < 0)
644       idPath = AddPath(path);
645     if (idPath < 0) return false;
646
647     CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
648     m_pDS->exec(strSQL.c_str());
649
650     return true;
651   }
652   catch (...)
653   {
654     CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
655   }
656
657   return false;
658 }
659
660 bool CVideoDatabase::LinkMovieToTvshow(int idMovie, int idShow, bool bRemove)
661 {
662    try
663   {
664     if (NULL == m_pDB.get()) return false;
665     if (NULL == m_pDS.get()) return false;
666
667     if (bRemove) // delete link
668     {
669       CStdString strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i and idShow=%i", idMovie, idShow);
670       m_pDS->exec(strSQL.c_str());
671       return true;
672     }
673
674     CStdString strSQL=PrepareSQL("insert into movielinktvshow (idShow,idMovie) values (%i,%i)", idShow,idMovie);
675     m_pDS->exec(strSQL.c_str());
676
677     return true;
678   }
679   catch (...)
680   {
681     CLog::Log(LOGERROR, "%s (%i, %i) failed", __FUNCTION__, idMovie, idShow);
682   }
683
684   return false;
685 }
686
687 bool CVideoDatabase::IsLinkedToTvshow(int idMovie)
688 {
689    try
690   {
691     if (NULL == m_pDB.get()) return false;
692     if (NULL == m_pDS.get()) return false;
693
694     CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
695     m_pDS->query(strSQL.c_str());
696     if (m_pDS->eof())
697     {
698       m_pDS->close();
699       return false;
700     }
701
702     m_pDS->close();
703     return true;
704   }
705   catch (...)
706   {
707     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
708   }
709
710   return false;
711 }
712
713 bool CVideoDatabase::GetLinksToTvShow(int idMovie, vector<int>& ids)
714 {
715    try
716   {
717     if (NULL == m_pDB.get()) return false;
718     if (NULL == m_pDS.get()) return false;
719
720     CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
721     m_pDS2->query(strSQL.c_str());
722     while (!m_pDS2->eof())
723     {
724       ids.push_back(m_pDS2->fv(1).get_asInt());
725       m_pDS2->next();
726     }
727
728     m_pDS2->close();
729     return true;
730   }
731   catch (...)
732   {
733     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
734   }
735
736   return false;
737 }
738
739
740 //********************************************************************************************************************************
741 int CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
742 {
743   try
744   {
745     if (NULL == m_pDB.get()) return -1;
746     if (NULL == m_pDS.get()) return -1;
747     CStdString strPath, strFileName;
748     SplitPath(strFilenameAndPath,strPath,strFileName);
749
750     int idPath = GetPathId(strPath);
751     if (idPath >= 0)
752     {
753       CStdString strSQL;
754       strSQL=PrepareSQL("select idFile from files where strFileName like '%s' and idPath=%i", strFileName.c_str(),idPath);
755       m_pDS->query(strSQL.c_str());
756       if (m_pDS->num_rows() > 0)
757       {
758         int idFile = m_pDS->fv("files.idFile").get_asInt();
759         m_pDS->close();
760         return idFile;
761       }
762     }
763   }
764   catch (...)
765   {
766     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
767   }
768   return -1;
769 }
770
771 int CVideoDatabase::GetFileId(const CFileItem &item)
772 {
773   if (item.IsVideoDb() && item.HasVideoInfoTag())
774     return GetFileId(item.GetVideoInfoTag()->m_strFileNameAndPath);
775   return GetFileId(item.m_strPath);
776 }
777
778 //********************************************************************************************************************************
779 int CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
780 {
781   try
782   {
783     if (NULL == m_pDB.get()) return -1;
784     if (NULL == m_pDS.get()) return -1;
785     int idMovie = -1;
786
787     // needed for query parameters
788     int idFile = GetFileId(strFilenameAndPath);
789     int idPath=-1;
790     CStdString strPath;
791     if (idFile < 0)
792     {
793       CStdString strFile;
794       SplitPath(strFilenameAndPath,strPath,strFile);
795
796       // have to join movieinfo table for correct results
797       idPath = GetPathId(strPath);
798       if (idPath < 0 && strPath != strFilenameAndPath)
799         return -1;
800     }
801
802     if (idFile == -1 && strPath != strFilenameAndPath)
803       return -1;
804
805     CStdString strSQL;
806     if (idFile == -1)
807       strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
808     else
809       strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
810
811     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
812     m_pDS->query(strSQL.c_str());
813     if (m_pDS->num_rows() > 0)
814       idMovie = m_pDS->fv("idMovie").get_asInt();
815     m_pDS->close();
816
817     return idMovie;
818   }
819   catch (...)
820   {
821     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
822   }
823   return -1;
824 }
825
826 int CVideoDatabase::GetTvShowId(const CStdString& strPath)
827 {
828   try
829   {
830     if (NULL == m_pDB.get()) return -1;
831     if (NULL == m_pDS.get()) return -1;
832     int idTvShow = -1;
833
834     // have to join movieinfo table for correct results
835     int idPath = GetPathId(strPath);
836     if (idPath < 0)
837       return -1;
838
839     CStdString strSQL;
840     CStdString strPath1=strPath;
841     CStdString strParent;
842     int iFound=0;
843
844     strSQL=PrepareSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%i",idPath);
845     m_pDS->query(strSQL);
846     if (!m_pDS->eof())
847       iFound = 1;
848
849     while (iFound == 0 && URIUtils::GetParentPath(strPath1, strParent))
850     {
851       strSQL=PrepareSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and strPath like '%s'",strParent.c_str());
852       m_pDS->query(strSQL.c_str());
853       if (!m_pDS->eof())
854       {
855         int idShow = m_pDS->fv("idShow").get_asInt();
856         if (idShow != -1)
857           iFound = 2;
858       }
859       strPath1 = strParent;
860     }
861
862     if (m_pDS->num_rows() > 0)
863       idTvShow = m_pDS->fv("idShow").get_asInt();
864     m_pDS->close();
865
866     return idTvShow;
867   }
868   catch (...)
869   {
870     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
871   }
872   return -1;
873 }
874
875 int CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, int idEpisode, int idSeason) // input value is episode/season number hint - for multiparters
876 {
877   try
878   {
879     if (NULL == m_pDB.get()) return -1;
880     if (NULL == m_pDS.get()) return -1;
881
882     // need this due to the nested GetEpisodeInfo query
883     auto_ptr<Dataset> pDS;
884     pDS.reset(m_pDB->CreateDataset());
885     if (NULL == pDS.get()) return -1;
886
887     int idFile = GetFileId(strFilenameAndPath);
888     if (idFile < 0)
889       return -1;
890
891     CStdString strSQL=PrepareSQL("select idEpisode from episode where idFile=%i", idFile);
892
893     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
894     pDS->query(strSQL.c_str());
895     if (pDS->num_rows() > 0)
896     {
897       if (idEpisode == -1)
898         idEpisode = pDS->fv("episode.idEpisode").get_asInt();
899       else // use the hint!
900       {
901         while (!pDS->eof())
902         {
903           CVideoInfoTag tag;
904           int idTmpEpisode = pDS->fv("episode.idEpisode").get_asInt();
905           GetEpisodeInfo(strFilenameAndPath,tag,idTmpEpisode);
906           if (tag.m_iEpisode == idEpisode && (idSeason == -1 || tag.m_iSeason == idSeason)) {
907             // match on the episode hint, and there's no season hint or a season hint match
908             idEpisode = idTmpEpisode;
909             break;
910           }
911           pDS->next();
912         }
913         if (pDS->eof())
914           idEpisode = -1;
915       }
916     }
917     else
918       idEpisode = -1;
919
920     pDS->close();
921
922     return idEpisode;
923   }
924   catch (...)
925   {
926     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
927   }
928   return -1;
929 }
930
931 int CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
932 {
933   try
934   {
935     if (NULL == m_pDB.get()) return -1;
936     if (NULL == m_pDS.get()) return -1;
937
938     int idFile = GetFileId(strFilenameAndPath);
939     if (idFile < 0)
940       return -1;
941
942     CStdString strSQL=PrepareSQL("select idMVideo from musicvideo where idFile=%i", idFile);
943
944     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
945     m_pDS->query(strSQL.c_str());
946     int idMVideo=-1;
947     if (m_pDS->num_rows() > 0)
948       idMVideo = m_pDS->fv("idMVideo").get_asInt();
949     m_pDS->close();
950
951     return idMVideo;
952   }
953   catch (...)
954   {
955     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
956   }
957   return -1;
958 }
959
960 //********************************************************************************************************************************
961 int CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
962 {
963   try
964   {
965     if (NULL == m_pDB.get()) return -1;
966     if (NULL == m_pDS.get()) return -1;
967
968     int idMovie = GetMovieId(strFilenameAndPath);
969     if (idMovie < 0)
970     {
971       int idFile = AddFile(strFilenameAndPath);
972       if (idFile < 0)
973         return -1;
974       CStdString strSQL=PrepareSQL("insert into movie (idMovie, idFile) values (NULL, %i)", idFile);
975       m_pDS->exec(strSQL.c_str());
976       idMovie = (int)m_pDS->lastinsertid();
977 //      CommitTransaction();
978     }
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::AddTvShow(const CStdString& strPath)
990 {
991   try
992   {
993     if (NULL == m_pDB.get()) return -1;
994     if (NULL == m_pDS.get()) return -1;
995
996     CStdString strSQL=PrepareSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath like '%s' and path.idPath=tvshowlinkpath.idPath",strPath.c_str());
997     m_pDS->query(strSQL.c_str());
998     if (m_pDS->num_rows() != 0)
999       return m_pDS->fv("tvshowlinkpath.idShow").get_asInt();
1000
1001     strSQL=PrepareSQL("insert into tvshow (idShow) values (NULL)");
1002     m_pDS->exec(strSQL.c_str());
1003     int idTvShow = (int)m_pDS->lastinsertid();
1004
1005     int idPath = GetPathId(strPath);
1006     if (idPath < 0)
1007       idPath = AddPath(strPath);
1008     strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
1009     m_pDS->exec(strSQL.c_str());
1010
1011 //    CommitTransaction();
1012
1013     return idTvShow;
1014   }
1015   catch (...)
1016   {
1017     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1018   }
1019   return -1;
1020 }
1021
1022 //********************************************************************************************************************************
1023 int CVideoDatabase::AddEpisode(int idShow, const CStdString& strFilenameAndPath)
1024 {
1025   try
1026   {
1027     if (NULL == m_pDB.get()) return -1;
1028     if (NULL == m_pDS.get()) return -1;
1029
1030     int idFile = AddFile(strFilenameAndPath);
1031     if (idFile < 0)
1032       return -1;
1033
1034     CStdString strSQL=PrepareSQL("insert into episode (idEpisode, idFile) values (NULL, %i)", idFile);
1035     m_pDS->exec(strSQL.c_str());
1036     int idEpisode = (int)m_pDS->lastinsertid();
1037
1038     strSQL=PrepareSQL("insert into tvshowlinkepisode (idShow,idEpisode) values (%i,%i)",idShow,idEpisode);
1039     m_pDS->exec(strSQL.c_str());
1040
1041 //    CommitTransaction();
1042
1043     return idEpisode;
1044   }
1045   catch (...)
1046   {
1047     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1048   }
1049   return -1;
1050 }
1051
1052 int CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
1053 {
1054   try
1055   {
1056     if (NULL == m_pDB.get()) return -1;
1057     if (NULL == m_pDS.get()) return -1;
1058
1059     int idMVideo = GetMusicVideoId(strFilenameAndPath);
1060     if (idMVideo < 0)
1061     {
1062       int idFile = AddFile(strFilenameAndPath);
1063       if (idFile < 0)
1064         return -1;
1065       CStdString strSQL=PrepareSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %i)", idFile);
1066       m_pDS->exec(strSQL.c_str());
1067       idMVideo = (int)m_pDS->lastinsertid();
1068     }
1069
1070     return idMVideo;
1071   }
1072   catch (...)
1073   {
1074     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1075   }
1076   return -1;
1077 }
1078
1079 //********************************************************************************************************************************
1080 int CVideoDatabase::AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value)
1081 {
1082   try
1083   {
1084     if (NULL == m_pDB.get()) return -1;
1085     if (NULL == m_pDS.get()) return -1;
1086
1087     CStdString strSQL = PrepareSQL("select %s from %s where %s like '%s'", firstField.c_str(), table.c_str(), secondField.c_str(), value.c_str());
1088     m_pDS->query(strSQL.c_str());
1089     if (m_pDS->num_rows() == 0)
1090     {
1091       m_pDS->close();
1092       // doesnt exists, add it
1093       strSQL = PrepareSQL("insert into %s (%s, %s) values( NULL, '%s')", table.c_str(), firstField.c_str(), secondField.c_str(), value.c_str());
1094       m_pDS->exec(strSQL.c_str());
1095       int id = (int)m_pDS->lastinsertid();
1096       return id;
1097     }
1098     else
1099     {
1100       int id = m_pDS->fv(firstField).get_asInt();
1101       m_pDS->close();
1102       return id;
1103     }
1104   }
1105   catch (...)
1106   {
1107     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, value.c_str() );
1108   }
1109
1110   return -1;
1111 }
1112
1113 int CVideoDatabase::AddSet(const CStdString& strSet)
1114 {
1115   return AddToTable("sets", "idSet", "strSet", strSet);
1116 }
1117
1118 int CVideoDatabase::AddGenre(const CStdString& strGenre)
1119 {
1120   return AddToTable("genre", "idGenre", "strGenre", strGenre);
1121 }
1122
1123 int CVideoDatabase::AddStudio(const CStdString& strStudio)
1124 {
1125   return AddToTable("studio", "idStudio", "strStudio", strStudio);
1126 }
1127
1128 //********************************************************************************************************************************
1129 int CVideoDatabase::AddCountry(const CStdString& strCountry)
1130 {
1131   return AddToTable("country", "idCountry", "strCountry", strCountry);
1132 }
1133
1134 int CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& strThumb)
1135 {
1136   try
1137   {
1138     if (NULL == m_pDB.get()) return -1;
1139     if (NULL == m_pDS.get()) return -1;
1140     CStdString strSQL=PrepareSQL("select idActor from actors where strActor like '%s'", strActor.c_str());
1141     m_pDS->query(strSQL.c_str());
1142     if (m_pDS->num_rows() == 0)
1143     {
1144       m_pDS->close();
1145       // doesnt exists, add it
1146       strSQL=PrepareSQL("insert into actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),strThumb.c_str());
1147       m_pDS->exec(strSQL.c_str());
1148       int idActor = (int)m_pDS->lastinsertid();
1149       return idActor;
1150     }
1151     else
1152     {
1153       const field_value value = m_pDS->fv("idActor");
1154       int idActor = value.get_asInt() ;
1155       // update the thumb url's
1156       if (!strThumb.IsEmpty())
1157         strSQL=PrepareSQL("update actors set strThumb='%s' where idActor=%i",strThumb.c_str(),idActor);
1158       m_pDS->close();
1159       return idActor;
1160     }
1161
1162   }
1163   catch (...)
1164   {
1165     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
1166   }
1167   return -1;
1168 }
1169
1170
1171
1172 void CVideoDatabase::AddLinkToActor(const char *table, int actorID, const char *secondField, int secondID, const CStdString &role, int order)
1173 {
1174   try
1175   {
1176     if (NULL == m_pDB.get()) return ;
1177     if (NULL == m_pDS.get()) return ;
1178
1179     CStdString strSQL=PrepareSQL("select * from %s where idActor=%i and %s=%i", table, actorID, secondField, secondID);
1180     m_pDS->query(strSQL.c_str());
1181     if (m_pDS->num_rows() == 0)
1182     {
1183       // doesnt exists, add it
1184       strSQL=PrepareSQL("insert into %s (idActor, %s, strRole, iOrder) values(%i,%i,'%s',%i)", table, secondField, actorID, secondID, role.c_str(), order);
1185       m_pDS->exec(strSQL.c_str());
1186     }
1187     m_pDS->close();
1188   }
1189   catch (...)
1190   {
1191     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1192   }
1193 }
1194
1195 void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID)
1196 {
1197   try
1198   {
1199     if (NULL == m_pDB.get()) return ;
1200     if (NULL == m_pDS.get()) return ;
1201
1202     CStdString strSQL=PrepareSQL("select * from %s where %s=%i and %s=%i", table, firstField, firstID, secondField, secondID);
1203     m_pDS->query(strSQL.c_str());
1204     if (m_pDS->num_rows() == 0)
1205     {
1206       // doesnt exists, add it
1207       strSQL=PrepareSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
1208       m_pDS->exec(strSQL.c_str());
1209     }
1210     m_pDS->close();
1211   }
1212   catch (...)
1213   {
1214     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1215   }
1216 }
1217
1218 //****Sets****
1219 void CVideoDatabase::AddSetToMovie(int idMovie, int idSet)
1220 {
1221   AddToLinkTable("setlinkmovie", "idSet", idSet, "idMovie", idMovie);
1222 }
1223
1224 //****Actors****
1225 void CVideoDatabase::AddActorToMovie(int idMovie, int idActor, const CStdString& strRole, int order)
1226 {
1227   AddLinkToActor("actorlinkmovie", idActor, "idMovie", idMovie, strRole, order);
1228 }
1229
1230 void CVideoDatabase::AddActorToTvShow(int idTvShow, int idActor, const CStdString& strRole, int order)
1231 {
1232   AddLinkToActor("actorlinktvshow", idActor, "idShow", idTvShow, strRole, order);
1233 }
1234
1235 void CVideoDatabase::AddActorToEpisode(int idEpisode, int idActor, const CStdString& strRole, int order)
1236 {
1237   AddLinkToActor("actorlinkepisode", idActor, "idEpisode", idEpisode, strRole, order);
1238 }
1239
1240 void CVideoDatabase::AddArtistToMusicVideo(int idMVideo, int idArtist)
1241 {
1242   AddToLinkTable("artistlinkmusicvideo", "idArtist", idArtist, "idMVideo", idMVideo);
1243 }
1244
1245 //****Directors + Writers****
1246 void CVideoDatabase::AddDirectorToMovie(int idMovie, int idDirector)
1247 {
1248   AddToLinkTable("directorlinkmovie", "idDirector", idDirector, "idMovie", idMovie);
1249 }
1250
1251 void CVideoDatabase::AddDirectorToTvShow(int idTvShow, int idDirector)
1252 {
1253   AddToLinkTable("directorlinktvshow", "idDirector", idDirector, "idShow", idTvShow);
1254 }
1255
1256 void CVideoDatabase::AddWriterToEpisode(int idEpisode, int idWriter)
1257 {
1258   AddToLinkTable("writerlinkepisode", "idWriter", idWriter, "idEpisode", idEpisode);
1259 }
1260
1261 void CVideoDatabase::AddWriterToMovie(int idMovie, int idWriter)
1262 {
1263   AddToLinkTable("writerlinkmovie", "idWriter", idWriter, "idMovie", idMovie);
1264 }
1265
1266 void CVideoDatabase::AddDirectorToEpisode(int idEpisode, int idDirector)
1267 {
1268   AddToLinkTable("directorlinkepisode", "idDirector", idDirector, "idEpisode", idEpisode);
1269 }
1270
1271 void CVideoDatabase::AddDirectorToMusicVideo(int idMVideo, int idDirector)
1272 {
1273   AddToLinkTable("directorlinkmusicvideo", "idDirector", idDirector, "idMVideo", idMVideo);
1274 }
1275
1276 //****Studios****
1277 void CVideoDatabase::AddStudioToMovie(int idMovie, int idStudio)
1278 {
1279   AddToLinkTable("studiolinkmovie", "idStudio", idStudio, "idMovie", idMovie);
1280 }
1281
1282 void CVideoDatabase::AddStudioToTvShow(int idTvShow, int idStudio)
1283 {
1284   AddToLinkTable("studiolinktvshow", "idStudio", idStudio, "idShow", idTvShow);
1285 }
1286
1287 void CVideoDatabase::AddStudioToMusicVideo(int idMVideo, int idStudio)
1288 {
1289   AddToLinkTable("studiolinkmusicvideo", "idStudio", idStudio, "idMVideo", idMVideo);
1290 }
1291
1292 //****Genres****
1293 void CVideoDatabase::AddGenreToMovie(int idMovie, int idGenre)
1294 {
1295   AddToLinkTable("genrelinkmovie", "idGenre", idGenre, "idMovie", idMovie);
1296 }
1297
1298 void CVideoDatabase::AddGenreToTvShow(int idTvShow, int idGenre)
1299 {
1300   AddToLinkTable("genrelinktvshow", "idGenre", idGenre, "idShow", idTvShow);
1301 }
1302
1303 void CVideoDatabase::AddGenreToMusicVideo(int idMVideo, int idGenre)
1304 {
1305   AddToLinkTable("genrelinkmusicvideo", "idGenre", idGenre, "idMVideo", idMVideo);
1306 }
1307
1308 //****Country****
1309 void CVideoDatabase::AddCountryToMovie(int idMovie, int idCountry)
1310 {
1311   AddToLinkTable("countrylinkmovie", "idCountry", idCountry, "idMovie", idMovie);
1312 }
1313
1314 //********************************************************************************************************************************
1315 bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
1316 {
1317   try
1318   {
1319     if (NULL == m_pDB.get()) return false;
1320     if (NULL == m_pDS.get()) return false;
1321     int idMovie = GetMovieId(strFilenameAndPath);
1322     return (idMovie > 0); // index of zero is also invalid
1323
1324     // work in progress
1325     if (idMovie > 0)
1326     {
1327       // get title.  if no title, the id was "deleted" for in-place update
1328       CVideoInfoTag details;
1329       GetMovieInfo(strFilenameAndPath, details, idMovie);
1330       if (!details.m_strTitle.IsEmpty()) return true;
1331     }
1332     return false;
1333   }
1334   catch (...)
1335   {
1336     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1337   }
1338   return false;
1339 }
1340
1341 bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
1342 {
1343   try
1344   {
1345     if (NULL == m_pDB.get()) return false;
1346     if (NULL == m_pDS.get()) return false;
1347     int idTvShow = GetTvShowId(strPath);
1348     return (idTvShow > 0); // index of zero is also invalid
1349
1350     // work in progress
1351     if (idTvShow > 0)
1352     {
1353       // get title. if no title, the id was "deleted" for in-place update
1354       CVideoInfoTag details;
1355       GetTvShowInfo(strPath, details, idTvShow);
1356       if (!details.m_strTitle.IsEmpty()) return true;
1357     }
1358     return false;
1359   }
1360   catch (...)
1361   {
1362     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1363   }
1364   return false;
1365 }
1366
1367 bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
1368 {
1369   try
1370   {
1371     if (NULL == m_pDB.get()) return false;
1372     if (NULL == m_pDS.get()) return false;
1373     int idEpisode = GetEpisodeId(strFilenameAndPath);
1374     return (idEpisode > 0); // index of zero is also invalid
1375
1376     // work in progress
1377     if (idEpisode > 0)
1378     {
1379       // get title.  if no title, the id was "deleted" for in-place update
1380       CVideoInfoTag details;
1381       GetEpisodeInfo(strFilenameAndPath, details, idEpisode);
1382       if (!details.m_strTitle.IsEmpty()) return true;
1383     }
1384     return false;
1385   }
1386   catch (...)
1387   {
1388     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1389   }
1390   return false;
1391 }
1392
1393 bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
1394 {
1395   try
1396   {
1397     if (NULL == m_pDB.get()) return false;
1398     if (NULL == m_pDS.get()) return false;
1399     int idMVideo = GetMusicVideoId(strFilenameAndPath);
1400     return (idMVideo > 0); // index of zero is also invalid
1401
1402     // work in progress
1403     if (idMVideo > 0)
1404     {
1405       // get title.  if no title, the id was "deleted" for in-place update
1406       CVideoInfoTag details;
1407       GetMusicVideoInfo(strFilenameAndPath, details, idMVideo);
1408       if (!details.m_strTitle.IsEmpty()) return true;
1409     }
1410     return false;
1411   }
1412   catch (...)
1413   {
1414     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1415   }
1416   return false;
1417 }
1418
1419 void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath)
1420 {// TODO: merge into DeleteTvShow
1421   try
1422   {
1423     if (NULL == m_pDB.get()) return ;
1424     if (NULL == m_pDS.get()) return ;
1425
1426     int idTvShow = GetTvShowId(strPath);
1427     if ( idTvShow < 0) return ;
1428
1429     CFileItemList items;
1430     CStdString strPath2;
1431     strPath2.Format("videodb://2/2/%i/",idTvShow);
1432     GetSeasonsNav(strPath2,items,-1,-1,-1,-1,idTvShow);
1433     for( int i=0;i<items.Size();++i )
1434       CTextureCache::Get().ClearCachedImage(items[i]->GetCachedSeasonThumb(), true);
1435     DeleteThumbForItem(strPath,true);
1436
1437     CStdString strSQL;
1438     strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
1439     m_pDS->exec(strSQL.c_str());
1440
1441     strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
1442     m_pDS->exec(strSQL.c_str());
1443
1444     strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
1445     m_pDS->exec(strSQL.c_str());
1446
1447     strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
1448     m_pDS->exec(strSQL.c_str());
1449
1450     // remove all info other than the id
1451     // we do this due to the way we have the link between the file + movie tables.
1452
1453     strSQL = "update tvshow set ";
1454     for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1455     {
1456       CStdString column;
1457       column.Format("c%02d=NULL,", iType);
1458       strSQL += column;
1459     }
1460     strSQL = strSQL.Mid(0, strSQL.size() - 1) + PrepareSQL(" where idShow=%i", idTvShow);
1461     m_pDS->exec(strSQL.c_str());
1462   }
1463   catch (...)
1464   {
1465     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1466   }
1467 }
1468
1469 //********************************************************************************************************************************
1470 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
1471 {
1472   CStdString where = PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie=movieview.idMovie "
1473                                 "join actors on actors.idActor=actorlinkmovie.idActor "
1474                                 "where actors.strActor='%s'", strActor.c_str());
1475   GetMoviesByWhere("videodb://1/2/", where, "", items);
1476 }
1477
1478 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
1479 {
1480   CStdString where = PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow=tvshowview.idShow "
1481                                "join actors on actors.idActor=actorlinktvshow.idActor "
1482                                "where actors.strActor='%s'", strActor.c_str());
1483   GetTvShowsByWhere("videodb://2/2/", where, items);
1484 }
1485
1486 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
1487 {
1488   CStdString where = PrepareSQL("join actorlinkepisode on actorlinkepisode.idEpisode=episodeview.idEpisode "
1489                                "join actors on actors.idActor=actorlinkepisode.idActor "
1490                                "where actors.strActor='%s'", strActor.c_str());
1491   GetEpisodesByWhere("videodb://2/2/", where, items);
1492 }
1493
1494 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1495 {
1496   try
1497   {
1498     items.Clear();
1499     if (NULL == m_pDB.get()) return ;
1500     if (NULL == m_pDS.get()) return ;
1501
1502     CStdString strSQL;
1503     if (strArtist.IsEmpty())  // TODO: SMARTPLAYLISTS what is this here for???
1504       strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1505     else
1506       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());
1507     m_pDS->query( strSQL.c_str() );
1508
1509     while (!m_pDS->eof())
1510     {
1511       CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1512       CFileItemPtr pItem(new CFileItem(tag));
1513       pItem->SetLabel(tag.m_strArtist);
1514       items.Add(pItem);
1515       m_pDS->next();
1516     }
1517     m_pDS->close();
1518   }
1519   catch (...)
1520   {
1521     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1522   }
1523 }
1524
1525 //********************************************************************************************************************************
1526 void CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
1527 {
1528   try
1529   {
1530     // TODO: Optimize this - no need for all the queries!
1531     if (idMovie < 0)
1532       idMovie = GetMovieId(strFilenameAndPath);
1533     if (idMovie < 0) return ;
1534
1535     CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
1536     if (!m_pDS->query(sql.c_str()))
1537       return;
1538     details = GetDetailsForMovie(m_pDS, true);
1539   }
1540   catch (...)
1541   {
1542     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1543   }
1544 }
1545
1546 //********************************************************************************************************************************
1547 void CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
1548 {
1549   try
1550   {
1551     if (idTvShow < 0)
1552       idTvShow = GetTvShowId(strPath);
1553     if (idTvShow < 0) return ;
1554
1555     CStdString sql = PrepareSQL("SELECT * FROM tvshowview WHERE idShow=%i", idTvShow);
1556     if (!m_pDS->query(sql.c_str()))
1557       return;
1558     details = GetDetailsForTvShow(m_pDS, true);
1559   }
1560   catch (...)
1561   {
1562     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1563   }
1564 }
1565
1566 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
1567 {
1568   try
1569   {
1570     // TODO: Optimize this - no need for all the queries!
1571     if (idEpisode < 0)
1572       idEpisode = GetEpisodeId(strFilenameAndPath);
1573     if (idEpisode < 0) return false;
1574
1575     CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
1576     if (!m_pDS->query(sql.c_str()))
1577       return false;
1578     details = GetDetailsForEpisode(m_pDS, true);
1579     return true;
1580   }
1581   catch (...)
1582   {
1583     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1584   }
1585   return false;
1586 }
1587
1588 void CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
1589 {
1590   try
1591   {
1592     // TODO: Optimize this - no need for all the queries!
1593     if (idMVideo < 0)
1594       idMVideo = GetMusicVideoId(strFilenameAndPath);
1595     if (idMVideo < 0) return ;
1596
1597     CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
1598     if (!m_pDS->query(sql.c_str()))
1599       return;
1600     details = GetDetailsForMusicVideo(m_pDS);
1601   }
1602   catch (...)
1603   {
1604     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1605   }
1606 }
1607
1608 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1609 {
1610   // add all directors
1611   if (!details.m_strDirector.IsEmpty())
1612   {
1613     CStdStringArray directors;
1614     StringUtils::SplitString(details.m_strDirector, g_advancedSettings.m_videoItemSeparator, directors);
1615     for (unsigned int i = 0; i < directors.size(); i++)
1616     {
1617       CStdString strDirector(directors[i]);
1618       strDirector.Trim();
1619       int idDirector = AddActor(strDirector,"");
1620       vecDirectors.push_back(idDirector);
1621     }
1622   }
1623
1624   // add all genres
1625   if (!details.m_strGenre.IsEmpty())
1626   {
1627     CStdStringArray genres;
1628     StringUtils::SplitString(details.m_strGenre, g_advancedSettings.m_videoItemSeparator, genres);
1629     for (unsigned int i = 0; i < genres.size(); i++)
1630     {
1631       CStdString strGenre(genres[i]);
1632       strGenre.Trim();
1633       int idGenre = AddGenre(strGenre);
1634       vecGenres.push_back(idGenre);
1635     }
1636   }
1637   // add all studios
1638   if (!details.m_strStudio.IsEmpty())
1639   {
1640     CStdStringArray studios;
1641     StringUtils::SplitString(details.m_strStudio, g_advancedSettings.m_videoItemSeparator, studios);
1642     for (unsigned int i = 0; i < studios.size(); i++)
1643     {
1644       CStdString strStudio(studios[i]);
1645       strStudio.Trim();
1646       int idStudio = AddStudio(strStudio);
1647       vecStudios.push_back(idStudio);
1648     }
1649   }
1650 }
1651
1652 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1653 {
1654   CStdString sql;
1655   for (int i = min + 1; i < max; ++i)
1656   {
1657     switch (offsets[i].type)
1658     {
1659     case VIDEODB_TYPE_STRING:
1660       sql += PrepareSQL("c%02d='%s',", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str());
1661       break;
1662     case VIDEODB_TYPE_INT:
1663       sql += PrepareSQL("c%02d='%i',", i, *(int*)(((char*)&details)+offsets[i].offset));
1664       break;
1665     case VIDEODB_TYPE_COUNT:
1666       {
1667         int value = *(int*)(((char*)&details)+offsets[i].offset);
1668         if (value)
1669           sql += PrepareSQL("c%02d=%i,", i, value);
1670         else
1671           sql += PrepareSQL("c%02d=NULL,", i);
1672       }
1673       break;
1674     case VIDEODB_TYPE_BOOL:
1675       sql += PrepareSQL("c%02d='%s',", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false");
1676       break;
1677     case VIDEODB_TYPE_FLOAT:
1678       sql += PrepareSQL("c%02d='%f',", i, *(float*)(((char*)&details)+offsets[i].offset));
1679       break;
1680     }
1681   }
1682   sql.TrimRight(',');
1683   return sql;
1684 }
1685
1686 //********************************************************************************************************************************
1687 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details)
1688 {
1689   try
1690   {
1691     CVideoInfoTag info = details;
1692
1693     int idMovie = GetMovieId(strFilenameAndPath);
1694     if (idMovie > -1)
1695       DeleteMovie(strFilenameAndPath, true); // true to keep the table entry
1696
1697     BeginTransaction();
1698
1699     idMovie = AddMovie(strFilenameAndPath);
1700     if (idMovie < 0)
1701     {
1702       CommitTransaction();
1703       return idMovie;
1704     }
1705
1706     vector<int> vecDirectors;
1707     vector<int> vecGenres;
1708     vector<int> vecStudios;
1709     AddGenreAndDirectorsAndStudios(info,vecDirectors,vecGenres,vecStudios);
1710
1711     for (unsigned int i = 0; i < vecGenres.size(); ++i)
1712       AddGenreToMovie(idMovie, vecGenres[i]);
1713
1714     for (unsigned int i = 0; i < vecDirectors.size(); ++i)
1715       AddDirectorToMovie(idMovie, vecDirectors[i]);
1716
1717     for (unsigned int i = 0; i < vecStudios.size(); ++i)
1718       AddStudioToMovie(idMovie, vecStudios[i]);
1719
1720     // add writers...
1721     if (!info.m_strWritingCredits.IsEmpty())
1722     {
1723       CStdStringArray writers;
1724       StringUtils::SplitString(info.m_strWritingCredits, g_advancedSettings.m_videoItemSeparator, writers);
1725       for (unsigned int i = 0; i < writers.size(); i++)
1726       {
1727         CStdString writer(writers[i]);
1728         writer.Trim();
1729         int idWriter = AddActor(writer,"");
1730         AddWriterToMovie(idMovie, idWriter );
1731       }
1732     }
1733
1734     // add cast...
1735     int order = 0;
1736     for (CVideoInfoTag::iCast it = info.m_cast.begin(); it != info.m_cast.end(); ++it)
1737     {
1738       int idActor = AddActor(it->strName,it->thumbUrl.m_xml);
1739       AddActorToMovie(idMovie, idActor, it->strRole, order++);
1740     }
1741
1742     // add sets...
1743     if (!info.m_strSet.IsEmpty())
1744     {
1745       CStdStringArray sets;
1746       StringUtils::SplitString(info.m_strSet, g_advancedSettings.m_videoItemSeparator, sets);
1747       for (unsigned int i = 0; i < sets.size(); i++)
1748       {
1749         CStdString set(sets[i]);
1750         set.Trim();
1751         int idSet = AddSet(set);
1752         AddSetToMovie(idMovie, idSet);
1753       }
1754     }
1755
1756     // add countries...
1757     if (!info.m_strCountry.IsEmpty())
1758     {
1759       CStdStringArray countries;
1760       StringUtils::SplitString(info.m_strCountry, g_advancedSettings.m_videoItemSeparator, countries);
1761       for (unsigned int i = 0; i < countries.size(); i++)
1762       {
1763         CStdString country(countries[i]);
1764         country.Trim();
1765         int idCountry = AddCountry(country);
1766         AddCountryToMovie(idMovie, idCountry);
1767       }
1768     }
1769
1770     if (details.HasStreamDetails())
1771       SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
1772
1773     // update our movie table (we know it was added already above)
1774     // and insert the new row
1775     CStdString sql = "update movie set " + GetValueString(info, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
1776     sql += PrepareSQL(" where idMovie=%i", idMovie);
1777     m_pDS->exec(sql.c_str());
1778     CommitTransaction();
1779
1780     AnnounceUpdate("movie", idMovie);
1781
1782     return idMovie;
1783   }
1784   catch (...)
1785   {
1786     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1787   }
1788   return -1;
1789 }
1790
1791 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details)
1792 {
1793   try
1794   {
1795     if (!m_pDB.get() || !m_pDS.get())
1796     {
1797       CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
1798       return -1;
1799     }
1800
1801     BeginTransaction();
1802
1803     int idTvShow = GetTvShowId(strPath);
1804     if (idTvShow < 0)
1805       idTvShow = AddTvShow(strPath);
1806
1807     vector<int> vecDirectors;
1808     vector<int> vecGenres;
1809     vector<int> vecStudios;
1810     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1811
1812     // add cast...
1813     int order = 0;
1814     for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
1815     {
1816       int idActor = AddActor(it->strName,it->thumbUrl.m_xml);
1817       AddActorToTvShow(idTvShow, idActor, it->strRole, order++);
1818     }
1819
1820     unsigned int i;
1821     for (i = 0; i < vecGenres.size(); ++i)
1822     {
1823       AddGenreToTvShow(idTvShow, vecGenres[i]);
1824     }
1825
1826     for (i = 0; i < vecDirectors.size(); ++i)
1827     {
1828       AddDirectorToTvShow(idTvShow, vecDirectors[i]);
1829     }
1830
1831     for (i = 0; i < vecStudios.size(); ++i)
1832     {
1833       AddStudioToTvShow(idTvShow, vecStudios[i]);
1834     }
1835
1836     // and insert the new row
1837     CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
1838     sql += PrepareSQL("where idShow=%i", idTvShow);
1839     m_pDS->exec(sql.c_str());
1840     CommitTransaction();
1841
1842     AnnounceUpdate("tvshow", idTvShow);
1843
1844     return idTvShow;
1845   }
1846   catch (...)
1847   {
1848     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1849   }
1850
1851   return -1;
1852 }
1853
1854 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, int idShow, int idEpisode)
1855 {
1856   try
1857   {
1858     BeginTransaction();
1859     if (idEpisode == -1)
1860     {
1861       idEpisode = GetEpisodeId(strFilenameAndPath);
1862       if (idEpisode > 0)
1863         DeleteEpisode(strFilenameAndPath,idEpisode);
1864
1865       idEpisode = AddEpisode(idShow,strFilenameAndPath);
1866       if (idEpisode < 0)
1867       {
1868         CommitTransaction();
1869         return -1;
1870       }
1871     }
1872
1873     vector<int> vecDirectors;
1874     vector<int> vecGenres;
1875     vector<int> vecStudios;
1876     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1877
1878     // add cast...
1879     int order = 0;
1880     for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
1881     {
1882       int idActor = AddActor(it->strName,it->thumbUrl.m_xml);
1883       AddActorToEpisode(idEpisode, idActor, it->strRole, order++);
1884     }
1885
1886     // add writers...
1887     if (!details.m_strWritingCredits.IsEmpty())
1888     {
1889       CStdStringArray writers;
1890       StringUtils::SplitString(details.m_strWritingCredits, g_advancedSettings.m_videoItemSeparator, writers);
1891       for (unsigned int i = 0; i < writers.size(); i++)
1892       {
1893         CStdString writer(writers[i]);
1894         writer.Trim();
1895         int idWriter = AddActor(writer,"");
1896         AddWriterToEpisode(idEpisode, idWriter );
1897       }
1898     }
1899
1900     for (unsigned int i = 0; i < vecDirectors.size(); ++i)
1901     {
1902       AddDirectorToEpisode(idEpisode, vecDirectors[i]);
1903     }
1904
1905     if (details.HasStreamDetails())
1906     {
1907       if (details.m_iFileId != -1)
1908         SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
1909       else
1910         SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
1911     }
1912
1913     // and insert the new row
1914     CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
1915     sql += PrepareSQL("where idEpisode=%i", idEpisode);
1916     m_pDS->exec(sql.c_str());
1917     CommitTransaction();
1918
1919     AnnounceUpdate("episode", idEpisode);
1920
1921     return idEpisode;
1922   }
1923   catch (...)
1924   {
1925     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1926   }
1927   return -1;
1928 }
1929
1930 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details)
1931 {
1932   try
1933   {
1934     BeginTransaction();
1935
1936     int idMVideo = GetMusicVideoId(strFilenameAndPath);
1937     if (idMVideo > -1)
1938     {
1939       DeleteMusicVideo(strFilenameAndPath);
1940     }
1941     idMVideo = AddMusicVideo(strFilenameAndPath);
1942     if (idMVideo < 0)
1943     {
1944       CommitTransaction();
1945       return -1;
1946     }
1947
1948     vector<int> vecDirectors;
1949     vector<int> vecGenres;
1950     vector<int> vecStudios;
1951     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1952
1953     // add artists...
1954     if (!details.m_strArtist.IsEmpty())
1955     {
1956       CStdStringArray vecArtists;
1957       StringUtils::SplitString(details.m_strArtist, g_advancedSettings.m_videoItemSeparator, vecArtists);
1958       for (unsigned int i = 0; i < vecArtists.size(); i++)
1959       {
1960         CStdString artist = vecArtists[i];
1961         artist.Trim();
1962         int idArtist = AddActor(artist,"");
1963         AddArtistToMusicVideo(idMVideo, idArtist);
1964       }
1965     }
1966
1967     unsigned int i;
1968     for (i = 0; i < vecGenres.size(); ++i)
1969     {
1970       AddGenreToMusicVideo(idMVideo, vecGenres[i]);
1971     }
1972
1973     for (i = 0; i < vecDirectors.size(); ++i)
1974     {
1975       AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
1976     }
1977
1978     for (i = 0; i < vecStudios.size(); ++i)
1979     {
1980       AddStudioToMusicVideo(idMVideo, vecStudios[i]);
1981     }
1982
1983     if (details.HasStreamDetails())
1984       SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
1985
1986     // update our movie table (we know it was added already above)
1987     // and insert the new row
1988     CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
1989     sql += PrepareSQL(" where idMVideo=%i", idMVideo);
1990     m_pDS->exec(sql.c_str());
1991     CommitTransaction();
1992
1993     AnnounceUpdate("musicvideo", idMVideo);
1994
1995     return idMVideo;
1996   }
1997   catch (...)
1998   {
1999     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2000   }
2001   return -1;
2002 }
2003
2004 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2005 {
2006   // AddFile checks to make sure the file isn't already in the DB first
2007   int idFile = AddFile(strFileNameAndPath);
2008   if (idFile < 0)
2009     return;
2010   SetStreamDetailsForFileId(details, idFile);
2011 }
2012
2013 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2014 {
2015   if (idFile < 0)
2016     return;
2017
2018   try
2019   {
2020     BeginTransaction();
2021     m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2022
2023     for (int i=1; i<=details.GetVideoStreamCount(); i++)
2024     {
2025       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2026         "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration) "
2027         "VALUES (%i,%i,'%s',%f,%i,%i,%i)",
2028         idFile, (int)CStreamDetail::VIDEO,
2029         details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2030         details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i)));
2031     }
2032     for (int i=1; i<=details.GetAudioStreamCount(); i++)
2033     {
2034       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2035         "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2036         "VALUES (%i,%i,'%s',%i,'%s')",
2037         idFile, (int)CStreamDetail::AUDIO,
2038         details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2039         details.GetAudioLanguage(i).c_str()));
2040     }
2041     for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2042     {
2043       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2044         "(idFile, iStreamType, strSubtitleLanguage) "
2045         "VALUES (%i,%i,'%s')",
2046         idFile, (int)CStreamDetail::SUBTITLE,
2047         details.GetSubtitleLanguage(i).c_str()));
2048     }
2049
2050     CommitTransaction();
2051   }
2052   catch (...)
2053   {
2054     RollbackTransaction();
2055     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2056   }
2057 }
2058
2059 //********************************************************************************************************************************
2060 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2061 {
2062   try
2063   {
2064     if (NULL == m_pDB.get()) return ;
2065     if (NULL == m_pDS.get()) return ;
2066
2067     if (idMovie < 0) return ;
2068
2069     CStdString strSQL;
2070     if (iType == VIDEODB_CONTENT_MOVIES)
2071       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 );
2072     if (iType == VIDEODB_CONTENT_EPISODES)
2073       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 );
2074     if (iType == VIDEODB_CONTENT_TVSHOWS)
2075       strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2076     if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2077       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 );
2078
2079     m_pDS->query( strSQL.c_str() );
2080     if (!m_pDS->eof())
2081     {
2082       if (iType != VIDEODB_CONTENT_TVSHOWS)
2083       {
2084         CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2085         ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2086       }
2087       else
2088         filePath = m_pDS->fv("path.strPath").get_asString();
2089     }
2090     m_pDS->close();
2091   }
2092   catch (...)
2093   {
2094     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2095   }
2096 }
2097
2098 //********************************************************************************************************************************
2099 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend)
2100 {
2101   try
2102   {
2103     int idFile = GetFileId(strFilenameAndPath);
2104     if (idFile < 0) return ;
2105     if (!bAppend)
2106       bookmarks.erase(bookmarks.begin(), bookmarks.end());
2107     if (NULL == m_pDB.get()) return ;
2108     if (NULL == m_pDS.get()) return ;
2109
2110     CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2111     m_pDS->query( strSQL.c_str() );
2112     while (!m_pDS->eof())
2113     {
2114       CBookmark bookmark;
2115       bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2116       bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2117       bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2118       bookmark.playerState = m_pDS->fv("playerState").get_asString();
2119       bookmark.player = m_pDS->fv("player").get_asString();
2120       bookmark.type = type;
2121       if (type == CBookmark::EPISODE)
2122       {
2123         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);
2124         m_pDS2->query(strSQL2.c_str());
2125         bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2126         bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2127         m_pDS2->close();
2128       }
2129       bookmarks.push_back(bookmark);
2130       m_pDS->next();
2131     }
2132     //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2133     m_pDS->close();
2134   }
2135   catch (...)
2136   {
2137     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2138   }
2139 }
2140
2141 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2142 {
2143   VECBOOKMARKS bookmarks;
2144   GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2145   if (bookmarks.size() > 0)
2146   {
2147     bookmark = bookmarks[0];
2148     return true;
2149   }
2150   return false;
2151 }
2152
2153 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2154 {
2155   if (!m_pDB.get() || !m_pDS.get())
2156     return;
2157
2158   int fileID = GetFileId(strFilenameAndPath);
2159   if (fileID < -1)
2160     return;
2161
2162   try
2163   {
2164     CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2165     m_pDS->exec(sql.c_str());
2166   }
2167   catch(...)
2168   {
2169     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2170   }
2171 }
2172
2173 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2174 {
2175   try
2176   {
2177     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);
2178     m_pDS->query(strSQL.c_str());
2179     while (!m_pDS->eof())
2180     {
2181       episodes.push_back(GetDetailsForEpisode(m_pDS));
2182       m_pDS->next();
2183     }
2184     m_pDS->close();
2185   }
2186   catch (...)
2187   {
2188     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2189   }
2190 }
2191
2192 //********************************************************************************************************************************
2193 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2194 {
2195   try
2196   {
2197     int idFile = AddFile(strFilenameAndPath);
2198     if (idFile < 0)
2199       return;
2200     if (NULL == m_pDB.get()) return ;
2201     if (NULL == m_pDS.get()) return ;
2202
2203     CStdString strSQL;
2204     int idBookmark=-1;
2205     if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2206     {
2207       strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2208     }
2209     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
2210     {
2211       /* get a bookmark within the same time as previous */
2212       double mintime = bookmark.timeInSeconds - 0.5f;
2213       double maxtime = bookmark.timeInSeconds + 0.5f;
2214       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());
2215     }
2216
2217     if (type != CBookmark::EPISODE)
2218     {
2219       // get current id
2220       m_pDS->query( strSQL.c_str() );
2221       if (m_pDS->num_rows() != 0)
2222         idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2223       m_pDS->close();
2224     }
2225     // update or insert depending if it existed before
2226     if (idBookmark >= 0 )
2227       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);
2228     else
2229       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);
2230
2231     m_pDS->exec(strSQL.c_str());
2232   }
2233   catch (...)
2234   {
2235     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2236   }
2237 }
2238
2239 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2240 {
2241   try
2242   {
2243     int idFile = GetFileId(strFilenameAndPath);
2244     if (idFile < 0) return ;
2245     if (NULL == m_pDB.get()) return ;
2246     if (NULL == m_pDS.get()) return ;
2247
2248     /* a litle bit uggly, we clear first bookmark that is within one second of given */
2249     /* should be no problem since we never add bookmarks that are closer than that   */
2250     double mintime = bookmark.timeInSeconds - 0.5f;
2251     double maxtime = bookmark.timeInSeconds + 0.5f;
2252     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);
2253
2254     m_pDS->query( strSQL.c_str() );
2255     if (m_pDS->num_rows() != 0)
2256     {
2257       int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2258       strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2259       m_pDS->exec(strSQL.c_str());
2260       if (type == CBookmark::EPISODE)
2261       {
2262         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);
2263         m_pDS->exec(strSQL.c_str());
2264       }
2265     }
2266
2267     m_pDS->close();
2268   }
2269   catch (...)
2270   {
2271     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2272   }
2273 }
2274
2275 //********************************************************************************************************************************
2276 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2277 {
2278   try
2279   {
2280     int idFile = GetFileId(strFilenameAndPath);
2281     if (idFile < 0) return ;
2282     if (NULL == m_pDB.get()) return ;
2283     if (NULL == m_pDS.get()) return ;
2284
2285     CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2286     m_pDS->exec(strSQL.c_str());
2287     if (type == CBookmark::EPISODE)
2288     {
2289       strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2290       m_pDS->exec(strSQL.c_str());
2291     }
2292   }
2293   catch (...)
2294   {
2295     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2296   }
2297 }
2298
2299
2300 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2301 {
2302   try
2303   {
2304     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);
2305     m_pDS->query( strSQL.c_str() );
2306     if (!m_pDS->eof())
2307     {
2308       bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2309       bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2310       bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2311       bookmark.playerState = m_pDS->fv("playerState").get_asString();
2312       bookmark.player = m_pDS->fv("player").get_asString();
2313       bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2314     }
2315     else
2316     {
2317       m_pDS->close();
2318       return false;
2319     }
2320     m_pDS->close();
2321   }
2322   catch (...)
2323   {
2324     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2325     return false;
2326   }
2327   return true;
2328 }
2329
2330 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2331 {
2332   try
2333   {
2334     int idFile = GetFileId(tag.m_strFileNameAndPath);
2335     // delete the current episode for the selected episode number
2336     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);
2337     m_pDS->exec(strSQL.c_str());
2338
2339     AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2340     int idBookmark = (int)m_pDS->lastinsertid();
2341     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);
2342     m_pDS->exec(strSQL.c_str());
2343   }
2344   catch (...)
2345   {
2346     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2347   }
2348 }
2349
2350 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2351 {
2352   try
2353   {
2354     CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2355     m_pDS->exec(strSQL.c_str());
2356     strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2357     m_pDS->exec(strSQL.c_str());
2358   }
2359   catch (...)
2360   {
2361     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2362   }
2363 }
2364
2365 //********************************************************************************************************************************
2366 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2367 {
2368   try
2369   {
2370     if (NULL == m_pDB.get()) return ;
2371     if (NULL == m_pDS.get()) return ;
2372     int idMovie = GetMovieId(strFilenameAndPath);
2373     if (idMovie < 0)
2374     {
2375       return ;
2376     }
2377
2378     BeginTransaction();
2379
2380     CStdString strSQL;
2381     strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2382     m_pDS->exec(strSQL.c_str());
2383
2384     strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2385     m_pDS->exec(strSQL.c_str());
2386
2387     strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2388     m_pDS->exec(strSQL.c_str());
2389
2390     strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2391     m_pDS->exec(strSQL.c_str());
2392
2393     strSQL=PrepareSQL("delete from setlinkmovie where idMovie=%i", idMovie);
2394     m_pDS->exec(strSQL.c_str());
2395
2396     strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2397     m_pDS->exec(strSQL.c_str());
2398
2399     if (!bKeepThumb)
2400       DeleteThumbForItem(strFilenameAndPath,false);
2401
2402     DeleteStreamDetails(GetFileId(strFilenameAndPath));
2403
2404     // keep the movie table entry, linking to tv shows, and bookmarks
2405     // so we can update the data in place
2406     // the ancilliary tables are still purged
2407     if (!bKeepId)
2408     {
2409       ClearBookMarksOfFile(strFilenameAndPath);
2410
2411       strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2412       m_pDS->exec(strSQL.c_str());
2413
2414       strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2415       m_pDS->exec(strSQL.c_str());
2416     }
2417     /*
2418     // work in progress
2419     else
2420     {
2421       // clear the title
2422       strSQL=PrepareSQL("update movie set c%02d=NULL where idMovie=%i", VIDEODB_ID_TITLE, idMovie);
2423       m_pDS->exec(strSQL.c_str());
2424     }
2425     */
2426
2427     CStdString strPath, strFileName;
2428     SplitPath(strFilenameAndPath,strPath,strFileName);
2429     InvalidatePathHash(strPath);
2430     CommitTransaction();
2431
2432     if (!bKeepId)
2433       AnnounceRemove("movie", idMovie);
2434   }
2435   catch (...)
2436   {
2437     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2438   }
2439 }
2440
2441 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2442 {
2443   try
2444   {
2445     int idTvShow=-1;
2446     if (NULL == m_pDB.get()) return ;
2447     if (NULL == m_pDS.get()) return ;
2448     idTvShow = GetTvShowId(strPath);
2449     if (idTvShow < 0)
2450     {
2451       return ;
2452     }
2453
2454     BeginTransaction();
2455
2456     CStdString strSQL=PrepareSQL("select tvshowlinkepisode.idEpisode,path.strPath,files.strFileName from tvshowlinkepisode,path,files,episode where tvshowlinkepisode.idShow=%i and tvshowlinkepisode.idEpisode=episode.idEpisode and episode.idFile=files.idFile and files.idPath=path.idPath",idTvShow);
2457     m_pDS2->query(strSQL.c_str());
2458     while (!m_pDS2->eof())
2459     {
2460       CStdString strFilenameAndPath;
2461       CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2462       CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2463       ConstructPath(strFilenameAndPath, strPath, strFileName);
2464       DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2465       m_pDS2->next();
2466     }
2467
2468     strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
2469     m_pDS->exec(strSQL.c_str());
2470
2471     strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
2472     m_pDS->exec(strSQL.c_str());
2473
2474     strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
2475     m_pDS->exec(strSQL.c_str());
2476
2477     strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2478     m_pDS->exec(strSQL.c_str());
2479
2480     strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
2481     m_pDS->exec(strSQL.c_str());
2482
2483     if (!bKeepThumb)
2484       DeleteThumbForItem(strPath,true);
2485
2486     // keep tvshow table and movielink table so we can update data in place
2487     if (!bKeepId)
2488     {
2489       strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2490       m_pDS->exec(strSQL.c_str());
2491
2492       strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2493       m_pDS->exec(strSQL.c_str());
2494     }
2495
2496     InvalidatePathHash(strPath);
2497
2498     CommitTransaction();
2499
2500     if (!bKeepId)
2501       AnnounceRemove("tvshow", idTvShow);
2502   }
2503   catch (...)
2504   {
2505     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2506   }
2507 }
2508
2509 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2510 {
2511   try
2512   {
2513     if (NULL == m_pDB.get()) return ;
2514     if (NULL == m_pDS.get()) return ;
2515     if (idEpisode < 0)
2516     {
2517       idEpisode = GetEpisodeId(strFilenameAndPath);
2518       if (idEpisode < 0)
2519       {
2520         return ;
2521       }
2522     }
2523
2524     CStdString strSQL;
2525     strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
2526     m_pDS->exec(strSQL.c_str());
2527
2528     strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
2529     m_pDS->exec(strSQL.c_str());
2530
2531     strSQL=PrepareSQL("select tvshowlinkepisode.idShow from tvshowlinkepisode where idEpisode=%i",idEpisode);
2532     m_pDS->query(strSQL.c_str());
2533
2534     strSQL=PrepareSQL("delete from tvshowlinkepisode where idEpisode=%i", idEpisode);
2535     m_pDS->exec(strSQL.c_str());
2536
2537     if (!bKeepThumb)
2538       DeleteThumbForItem(strFilenameAndPath, false, idEpisode);
2539
2540     DeleteStreamDetails(GetFileId(strFilenameAndPath));
2541
2542     // keep episode table entry and bookmarks so we can update the data in place
2543     // the ancilliary tables are still purged
2544     if (!bKeepId)
2545     {
2546       ClearBookMarksOfFile(strFilenameAndPath);
2547
2548       strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
2549       m_pDS->exec(strSQL.c_str());
2550     }
2551
2552     if (!bKeepId)
2553       AnnounceRemove("episode", idEpisode);
2554   }
2555   catch (...)
2556   {
2557     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2558   }
2559 }
2560
2561 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2562 {
2563   try
2564   {
2565     if (NULL == m_pDB.get()) return ;
2566     if (NULL == m_pDS.get()) return ;
2567     int idMVideo = GetMusicVideoId(strFilenameAndPath);
2568     if (idMVideo < 0)
2569     {
2570       return ;
2571     }
2572
2573     BeginTransaction();
2574
2575     CStdString strSQL;
2576     strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
2577     m_pDS->exec(strSQL.c_str());
2578
2579     strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
2580     m_pDS->exec(strSQL.c_str());
2581
2582     strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
2583     m_pDS->exec(strSQL.c_str());
2584
2585     strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
2586     m_pDS->exec(strSQL.c_str());
2587
2588     if (!bKeepThumb)
2589       DeleteThumbForItem(strFilenameAndPath,false);
2590
2591     DeleteStreamDetails(GetFileId(strFilenameAndPath));
2592
2593     // keep the music video table entry and bookmarks so we can update data in place
2594     // the ancilliary tables are still purged
2595     if (!bKeepId)
2596     {
2597       ClearBookMarksOfFile(strFilenameAndPath);
2598
2599       strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
2600       m_pDS->exec(strSQL.c_str());
2601     }
2602     /*
2603     // work in progress
2604     else
2605     {
2606       // clear the title
2607       strSQL=PrepareSQL("update musicvideo set c%02d=NULL where idMVideo=%i", VIDEODB_ID_MUSICVIDEO_TITLE, idMVideo);
2608       m_pDS->exec(strSQL.c_str());
2609     }
2610     */
2611
2612     CStdString strPath, strFileName;
2613     SplitPath(strFilenameAndPath,strPath,strFileName);
2614     InvalidatePathHash(strPath);
2615     CommitTransaction();
2616
2617     if (!bKeepId)
2618       AnnounceRemove("musicvideo", idMVideo);
2619   }
2620   catch (...)
2621   {
2622     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2623   }
2624 }
2625
2626 void CVideoDatabase::DeleteStreamDetails(int idFile)
2627 {
2628     m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
2629 }
2630
2631 void CVideoDatabase::DeleteSet(int idSet)
2632 {
2633   try
2634   {
2635     if (NULL == m_pDB.get()) return ;
2636     if (NULL == m_pDS.get()) return ;
2637
2638     CStdString strSQL;
2639     strSQL=PrepareSQL("delete from sets where idSet=%i", idSet);
2640     m_pDS->exec(strSQL.c_str());
2641     strSQL=PrepareSQL("delete from setlinkmovie where idSet=%i", idSet);
2642     m_pDS->exec(strSQL.c_str());
2643   }
2644   catch (...)
2645   {
2646     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
2647   }
2648 }
2649
2650 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
2651 {
2652   for (int i = min + 1; i < max; i++)
2653   {
2654     switch (offsets[i].type)
2655     {
2656     case VIDEODB_TYPE_STRING:
2657       *(CStdString*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+idxOffset).get_asString();
2658       break;
2659     case VIDEODB_TYPE_INT:
2660     case VIDEODB_TYPE_COUNT:
2661       *(int*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+idxOffset).get_asInt();
2662       break;
2663     case VIDEODB_TYPE_BOOL:
2664       *(bool*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+idxOffset).get_asBool();
2665       break;
2666     case VIDEODB_TYPE_FLOAT:
2667       *(float*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+idxOffset).get_asFloat();
2668       break;
2669     }
2670   }
2671 }
2672
2673 DWORD movieTime = 0;
2674 DWORD castTime = 0;
2675
2676 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
2677 {
2678   CVideoInfoTag details;
2679   details.Reset();
2680
2681   switch (type)
2682   {
2683     case VIDEODB_CONTENT_MOVIES:
2684       GetMovieInfo("", details, id);
2685       break;
2686     case VIDEODB_CONTENT_TVSHOWS:
2687       GetTvShowInfo("", details, id);
2688       break;
2689     case VIDEODB_CONTENT_EPISODES:
2690       GetEpisodeInfo("", details, id);
2691       break;
2692     case VIDEODB_CONTENT_MUSICVIDEOS:
2693       GetMusicVideoInfo("", details, id);
2694     default:
2695       break;
2696   }
2697
2698   return details;
2699 }
2700
2701 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
2702 {
2703   if (tag.m_iFileId < 0)
2704     return false;
2705
2706   bool retVal = false;
2707
2708   dbiplus::Dataset *pDS = m_pDB->CreateDataset();
2709   CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
2710   pDS->query(strSQL);
2711
2712   CStreamDetails& details = tag.m_streamDetails;
2713   details.Reset();
2714   while (!pDS->eof())
2715   {
2716     CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
2717     switch (e)
2718     {
2719     case CStreamDetail::VIDEO:
2720       {
2721         CStreamDetailVideo *p = new CStreamDetailVideo();
2722         p->m_strCodec = pDS->fv(2).get_asString();
2723         p->m_fAspect = pDS->fv(3).get_asFloat();
2724         p->m_iWidth = pDS->fv(4).get_asInt();
2725         p->m_iHeight = pDS->fv(5).get_asInt();
2726         p->m_iDuration = pDS->fv(10).get_asInt();
2727         details.AddStream(p);
2728         retVal = true;
2729         break;
2730       }
2731     case CStreamDetail::AUDIO:
2732       {
2733         CStreamDetailAudio *p = new CStreamDetailAudio();
2734         p->m_strCodec = pDS->fv(6).get_asString();
2735         if (pDS->fv(7).get_isNull())
2736           p->m_iChannels = -1;
2737         else
2738           p->m_iChannels = pDS->fv(7).get_asInt();
2739         p->m_strLanguage = pDS->fv(8).get_asString();
2740         details.AddStream(p);
2741         retVal = true;
2742         break;
2743       }
2744     case CStreamDetail::SUBTITLE:
2745       {
2746         CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
2747         p->m_strLanguage = pDS->fv(9).get_asString();
2748         details.AddStream(p);
2749         retVal = true;
2750         break;
2751       }
2752     }
2753
2754     pDS->next();
2755   }
2756
2757   pDS->close();
2758   details.DetermineBestStreams();
2759
2760   if (details.GetVideoDuration() > 0)
2761     tag.m_strRuntime.Format("%i", details.GetVideoDuration() / 60 );
2762
2763   return retVal;
2764 }
2765
2766 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2767 {
2768   CVideoInfoTag details;
2769   details.Reset();
2770
2771   DWORD time = CTimeUtils::GetTimeMS();
2772   int idMovie = pDS->fv(0).get_asInt();
2773
2774   GetDetailsFromDB(pDS, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
2775
2776   details.m_iDbId = idMovie;
2777   GetCommonDetails(pDS, details);
2778   movieTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2779
2780   GetStreamDetails(details);
2781
2782   if (needsCast)
2783   {
2784     // create cast string
2785     CStdString strSQL = PrepareSQL("SELECT actors.strActor,actorlinkmovie.strRole,actors.strThumb FROM actorlinkmovie,actors WHERE actorlinkmovie.idMovie=%i AND actorlinkmovie.idActor=actors.idActor ORDER BY actorlinkmovie.iOrder",idMovie);
2786     m_pDS2->query(strSQL.c_str());
2787     while (!m_pDS2->eof())
2788     {
2789       SActorInfo info;
2790       info.strName = m_pDS2->fv("actors.strActor").get_asString();
2791       info.strRole = m_pDS2->fv("actorlinkmovie.strRole").get_asString();
2792       info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2793       details.m_cast.push_back(info);
2794       m_pDS2->next();
2795     }
2796     castTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2797     details.m_strPictureURL.Parse();
2798
2799     // create sets string
2800     strSQL = PrepareSQL("SELECT sets.strSet FROM sets,setlinkmovie WHERE setlinkmovie.idMovie=%i AND setlinkmovie.idSet=sets.idSet ORDER BY sets.idSet",idMovie);
2801     m_pDS2->query(strSQL.c_str());
2802     while (!m_pDS2->eof())
2803     {
2804       CStdString setName = m_pDS2->fv("sets.strSet").get_asString();
2805       if (!details.m_strSet.IsEmpty())
2806         details.m_strSet += g_advancedSettings.m_videoItemSeparator;
2807       details.m_strSet += setName;
2808       m_pDS2->next();
2809     }
2810
2811     // create tvshowlink string
2812     vector<int> links;
2813     GetLinksToTvShow(idMovie,links);
2814     for (unsigned int i=0;i<links.size();++i)
2815     {
2816       strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
2817                          VIDEODB_ID_TV_TITLE,links[i]);
2818       m_pDS2->query(strSQL.c_str());
2819       if (!m_pDS2->eof())
2820       {
2821         if (!details.m_strShowLink.IsEmpty())
2822           details.m_strShowLink += g_advancedSettings.m_videoItemSeparator;
2823         details.m_strShowLink += m_pDS2->fv(0).get_asString();
2824       }
2825     }
2826     m_pDS2->close();
2827   }
2828   return details;
2829 }
2830
2831 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2832 {
2833   CVideoInfoTag details;
2834   details.Reset();
2835
2836   DWORD time = CTimeUtils::GetTimeMS();
2837   int idTvShow = pDS->fv(0).get_asInt();
2838
2839   GetDetailsFromDB(pDS, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
2840   details.m_iDbId = idTvShow;
2841   details.m_strPath = pDS->fv(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
2842   details.m_iEpisode = m_pDS->fv(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
2843   details.m_playCount = m_pDS->fv(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
2844   details.m_strShowTitle = details.m_strTitle;
2845
2846   movieTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2847
2848   if (needsCast)
2849   {
2850     // create cast string
2851     CStdString strSQL = PrepareSQL("select actors.strActor,actorlinktvshow.strRole,actors.strThumb from actorlinktvshow,actors where actorlinktvshow.idShow=%i and actorlinktvshow.idActor = actors.idActor ORDER BY actorlinktvshow.iOrder",idTvShow);
2852     m_pDS2->query(strSQL.c_str());
2853     while (!m_pDS2->eof())
2854     {
2855       SActorInfo info;
2856       info.strName = m_pDS2->fv("actors.strActor").get_asString();
2857       info.strRole = m_pDS2->fv("actorlinktvshow.strRole").get_asString();
2858       info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2859       details.m_cast.push_back(info);
2860       m_pDS2->next();
2861     }
2862     castTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2863     details.m_strPictureURL.Parse();
2864   }
2865   return details;
2866 }
2867
2868 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2869 {
2870   CVideoInfoTag details;
2871   details.Reset();
2872
2873   DWORD time = CTimeUtils::GetTimeMS();
2874   int idEpisode = pDS->fv(0).get_asInt();
2875
2876   GetDetailsFromDB(pDS, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
2877   details.m_iDbId = idEpisode;
2878   GetCommonDetails(pDS, details);
2879   movieTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2880
2881   details.m_strMPAARating = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
2882   details.m_strShowTitle = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
2883   details.m_strStudio = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString();
2884   details.m_strPremiered = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString();
2885
2886   GetStreamDetails(details);
2887
2888   if (needsCast)
2889   {
2890     set<int> actors;
2891     set<int>::iterator it;
2892
2893     // create cast string
2894     CStdString strSQL = PrepareSQL("select actors.idActor,actors.strActor,actorlinkepisode.strRole,actors.strThumb from actorlinkepisode,actors where actorlinkepisode.idEpisode=%i and actorlinkepisode.idActor = actors.idActor ORDER BY actorlinkepisode.iOrder",idEpisode);
2895     m_pDS2->query(strSQL.c_str());
2896     bool showCast=false;
2897     while (!m_pDS2->eof() || !showCast)
2898     {
2899       if (!m_pDS2->eof())
2900       {
2901         int idActor = m_pDS2->fv("actors.idActor").get_asInt();
2902         it = actors.find(idActor);
2903
2904         if (it == actors.end())
2905         {
2906           SActorInfo info;
2907           info.strName = m_pDS2->fv("actors.strActor").get_asString();
2908           info.strRole = m_pDS2->fv("actorlinkepisode.strRole").get_asString();
2909           info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2910           details.m_cast.push_back(info);
2911           actors.insert(idActor);
2912         }
2913         m_pDS2->next();
2914       }
2915       if (m_pDS2->eof() && !showCast)
2916       {
2917         showCast = true;
2918         int idShow = GetTvShowForEpisode(details.m_iDbId);
2919         if (idShow > -1)
2920         {
2921           strSQL = PrepareSQL("select actors.idActor,actors.strActor,actorlinktvshow.strRole,actors.strThumb from actorlinktvshow,actors where actorlinktvshow.idShow=%i and actorlinktvshow.idActor = actors.idActor ORDER BY actorlinktvshow.iOrder",idShow);
2922           m_pDS2->query(strSQL.c_str());
2923         }
2924       }
2925     }
2926     castTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2927     details.m_strPictureURL.Parse();
2928     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);
2929     m_pDS2->query(strSQL.c_str());
2930     if (!m_pDS2->eof())
2931       details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
2932     m_pDS2->close();
2933   }
2934   return details;
2935 }
2936
2937 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS)
2938 {
2939   CVideoInfoTag details;
2940   details.Reset();
2941
2942   DWORD time = CTimeUtils::GetTimeMS();
2943   int idMovie = pDS->fv(0).get_asInt();
2944
2945   GetDetailsFromDB(pDS, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
2946   details.m_iDbId = idMovie;
2947   GetCommonDetails(pDS, details);
2948   movieTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2949
2950   GetStreamDetails(details);
2951
2952   details.m_strPictureURL.Parse();
2953   return details;
2954 }
2955
2956 void CVideoDatabase::GetCommonDetails(auto_ptr<Dataset> &pDS, CVideoInfoTag &details)
2957 {
2958   details.m_iFileId = pDS->fv(VIDEODB_DETAILS_FILEID).get_asInt();
2959   details.m_strPath = pDS->fv(VIDEODB_DETAILS_PATH).get_asString();
2960   CStdString strFileName = pDS->fv(VIDEODB_DETAILS_FILE).get_asString();
2961   ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
2962   details.m_playCount = pDS->fv(VIDEODB_DETAILS_PLAYCOUNT).get_asInt();
2963   details.m_lastPlayed = pDS->fv(VIDEODB_DETAILS_LASTPLAYED).get_asString();
2964 }
2965
2966 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
2967 /// \retval Returns true if the settings exist, false otherwise.
2968 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
2969 {
2970   try
2971   {
2972     // obtain the FileID (if it exists)
2973 #ifdef NEW_VIDEODB_METHODS
2974     if (NULL == m_pDB.get()) return false;
2975     if (NULL == m_pDS.get()) return false;
2976     CStdString strPath, strFileName;
2977     URIUtils::Split(strFilenameAndPath, strPath, strFileName);
2978     CStdString strSQL=PrepareSQL("select * from settings, files, path where settings.idFile=files.idFile and path.idPath=files.idPath and path.strPath like '%s' and files.strFileName like '%s'", strPath.c_str() , strFileName.c_str());
2979 #else
2980     int idFile = GetFileId(strFilenameAndPath);
2981     if (idFile < 0) return false;
2982     if (NULL == m_pDB.get()) return false;
2983     if (NULL == m_pDS.get()) return false;
2984     // ok, now obtain the settings for this file
2985     CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
2986 #endif
2987     m_pDS->query( strSQL.c_str() );
2988     if (m_pDS->num_rows() > 0)
2989     { // get the video settings info
2990       settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
2991       settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
2992       settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
2993       settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
2994       settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
2995       settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
2996       settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
2997       settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
2998       settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
2999       settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3000       settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3001       settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3002       settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3003       settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3004       settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3005       settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3006       settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3007       settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3008       settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3009       settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3010       settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3011       settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3012       settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3013       settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3014       settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3015       settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3016       settings.m_SubtitleCached = false;
3017       m_pDS->close();
3018       return true;
3019     }
3020     m_pDS->close();
3021   }
3022   catch (...)
3023   {
3024     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3025   }
3026   return false;
3027 }
3028
3029 /// \brief Sets the settings for a particular video file
3030 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3031 {
3032   try
3033   {
3034     if (NULL == m_pDB.get()) return ;
3035     if (NULL == m_pDS.get()) return ;
3036     int idFile = AddFile(strFilenameAndPath);
3037     if (idFile < 0)
3038       return;
3039     CStdString strSQL;
3040     strSQL.Format("select * from settings where idFile=%i", idFile);
3041     m_pDS->query( strSQL.c_str() );
3042     if (m_pDS->num_rows() > 0)
3043     {
3044       m_pDS->close();
3045       // update the item
3046       strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3047                        "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3048                        "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,",
3049                        setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3050                        setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3051                        setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3052                        setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod);
3053       CStdString strSQL2;
3054       strSQL2=PrepareSQL("ResumeTime=%i,Crop=%i,CropLeft=%i,CropRight=%i,CropTop=%i,CropBottom=%i where idFile=%i\n", setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom, idFile);
3055       strSQL += strSQL2;
3056       m_pDS->exec(strSQL.c_str());
3057       return ;
3058     }
3059     else
3060     { // add the items
3061       m_pDS->close();
3062       strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3063                 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3064                 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3065                 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3066                 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod) "
3067               "VALUES ";
3068       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)",
3069                            idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3070                            setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3071                            setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3072                            setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3073                            setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod);
3074       m_pDS->exec(strSQL.c_str());
3075     }
3076   }
3077   catch (...)
3078   {
3079     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3080   }
3081 }
3082
3083 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3084 /// \retval Returns true if the stack times exist, false otherwise.
3085 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> &times)
3086 {
3087   try
3088   {
3089     // obtain the FileID (if it exists)
3090     int idFile = GetFileId(filePath);
3091     if (idFile < 0) return false;
3092     if (NULL == m_pDB.get()) return false;
3093     if (NULL == m_pDS.get()) return false;
3094     // ok, now obtain the settings for this file
3095     CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3096     m_pDS->query( strSQL.c_str() );
3097     if (m_pDS->num_rows() > 0)
3098     { // get the video settings info
3099       CStdStringArray timeString;
3100       int timeTotal = 0;
3101       StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3102       times.clear();
3103       for (unsigned int i = 0; i < timeString.size(); i++)
3104       {
3105         times.push_back(atoi(timeString[i].c_str()));
3106         timeTotal += atoi(timeString[i].c_str());
3107       }
3108       m_pDS->close();
3109       return (timeTotal > 0);
3110     }
3111     m_pDS->close();
3112   }
3113   catch (...)
3114   {
3115     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3116   }
3117   return false;
3118 }
3119
3120 /// \brief Sets the stack times for a particular video file
3121 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> &times)
3122 {
3123   try
3124   {
3125     if (NULL == m_pDB.get()) return ;
3126     if (NULL == m_pDS.get()) return ;
3127     int idFile = AddFile(filePath);
3128     if (idFile < 0)
3129       return;
3130
3131     // delete any existing items
3132     m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3133
3134     // add the items
3135     CStdString timeString;
3136     timeString.Format("%i", times[0]);
3137     for (unsigned int i = 1; i < times.size(); i++)
3138     {
3139       CStdString time;
3140       time.Format(",%i", times[i]);
3141       timeString += time;
3142     }
3143     m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3144   }
3145   catch (...)
3146   {
3147     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3148   }
3149 }
3150
3151 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
3152 {
3153   if(URIUtils::IsMultiPath(strPath))
3154   {
3155     vector<CStdString> paths;
3156     CMultiPathDirectory::GetPaths(strPath, paths);
3157
3158     for(unsigned i=0;i<paths.size();i++)
3159       RemoveContentForPath(paths[i], progress);
3160   }
3161
3162   try
3163   {
3164     if (NULL == m_pDB.get()) return ;
3165     if (NULL == m_pDS.get()) return ;
3166
3167     auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3168     CStdString strPath1(strPath);
3169     CStdString strSQL = PrepareSQL("select idPath,strContent,strPath from path where strPath like '%%%s%%'",strPath1.c_str());
3170     progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
3171     pDS->query(strSQL.c_str());
3172     if (progress)
3173     {
3174       progress->SetHeading(700);
3175       progress->SetLine(0, "");
3176       progress->SetLine(1, 313);
3177       progress->SetLine(2, 330);
3178       progress->SetPercentage(0);
3179       progress->StartModal();
3180       progress->ShowProgressBar(true);
3181     }
3182     int iCurr=0;
3183     int iMax = pDS->num_rows();
3184     while (!pDS->eof())
3185     {
3186       bool bMvidsChecked=false;
3187       if (progress)
3188       {
3189         progress->SetPercentage((int)((float)(iCurr++)/iMax*100.f));
3190         progress->Progress();
3191       }
3192       int idPath = pDS->fv("path.idPath").get_asInt();
3193       CStdString strCurrPath = pDS->fv("path.strPath").get_asString();
3194       if (HasTvShowInfo(strCurrPath))
3195         DeleteTvShow(strCurrPath);
3196       else
3197       {
3198         strSQL=PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i",idPath);
3199         m_pDS2->query(strSQL.c_str());
3200         if (m_pDS2->eof())
3201         {
3202           strSQL=PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i",idPath);
3203           m_pDS2->query(strSQL.c_str());
3204           bMvidsChecked = true;
3205         }
3206         while (!m_pDS2->eof())
3207         {
3208           CStdString strMoviePath;
3209           CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
3210           ConstructPath(strMoviePath, strCurrPath, strFileName);
3211           if (HasMovieInfo(strMoviePath))
3212             DeleteMovie(strMoviePath);
3213           if (HasMusicVideoInfo(strMoviePath))
3214             DeleteMusicVideo(strMoviePath);
3215           m_pDS2->next();
3216           if (m_pDS2->eof() && !bMvidsChecked)
3217           {
3218             strSQL=PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i",idPath);
3219             m_pDS2->query(strSQL.c_str());
3220             bMvidsChecked = true;
3221           }
3222         }
3223         m_pDS2->close();
3224       }
3225       pDS->next();
3226     }
3227     strSQL = PrepareSQL("update path set strContent = '', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where strPath like '%%%s%%'",strPath1.c_str());
3228     pDS->exec(strSQL.c_str());
3229   }
3230   catch (...)
3231   {
3232     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
3233   }
3234   if (progress)
3235     progress->Close();
3236 }
3237
3238 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
3239 {
3240   // if we have a multipath, set scraper for all contained paths too
3241   if(URIUtils::IsMultiPath(filePath))
3242   {
3243     vector<CStdString> paths;
3244     CMultiPathDirectory::GetPaths(filePath, paths);
3245
3246     for(unsigned i=0;i<paths.size();i++)
3247       SetScraperForPath(paths[i],scraper,settings);
3248   }
3249
3250   try
3251   {
3252     if (NULL == m_pDB.get()) return ;
3253     if (NULL == m_pDS.get()) return ;
3254     int idPath = GetPathId(filePath);
3255     if (idPath < 0)
3256     { // no path found - we have to add one
3257       idPath = AddPath(filePath);
3258       if (idPath < 0) return ;
3259     }
3260
3261     // Update
3262     CStdString strSQL;
3263     if (settings.exclude)
3264     { //NB See note in ::GetScraperForPath about strContent=='none'
3265       strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
3266     }
3267     else if(!scraper)
3268     { // catch clearing content, but not excluding
3269       strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
3270     }
3271     else
3272     {
3273       CStdString content = TranslateContent(scraper->Content());
3274       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);
3275     }
3276     m_pDS->exec(strSQL.c_str());
3277   }
3278   catch (...)
3279   {
3280     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3281   }
3282 }
3283
3284 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
3285 {
3286   try
3287   {
3288     if (NULL == m_pDB.get()) return false;
3289     if (NULL == m_pDS.get()) return false;
3290
3291     CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
3292     if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
3293       return false;
3294     bool found = m_pDS->fv(0).get_asInt() > 0;
3295     m_pDS->close();
3296     return found;
3297   }
3298   catch (...)
3299   {
3300     CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
3301   }
3302   return false;
3303 }
3304
3305 bool CVideoDatabase::UpdateOldVersion(int iVersion)
3306 {
3307   BeginTransaction();
3308
3309   try
3310   {
3311     if (iVersion < 35)
3312     {
3313       m_pDS->exec("alter table settings add NonLinStretch bool");
3314     }
3315     if (iVersion < 36)
3316     {
3317       m_pDS->exec("alter table path add exclude bool");
3318     }
3319     if (iVersion < 37)
3320     {
3321       //recreate tables
3322       CStdString columnsSelect, columns;
3323       for (int i = 0; i < 21; i++)
3324       {
3325         CStdString column;
3326         column.Format(",c%02d", i);
3327         columnsSelect += column;
3328         columns += column + " text";
3329       }
3330
3331       //movie
3332       m_pDS->exec(PrepareSQL("CREATE TABLE movienew ( idMovie integer primary key, idFile integer%s)", columns.c_str()));
3333       m_pDS->exec(PrepareSQL("INSERT INTO movienew select idMovie,idFile%s from movie", columnsSelect.c_str()));
3334       m_pDS->exec("DROP TABLE movie");
3335       m_pDS->exec("ALTER TABLE movienew RENAME TO movie");
3336
3337       m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
3338       m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
3339
3340       //episode
3341       m_pDS->exec(PrepareSQL("CREATE TABLE episodenew ( idEpisode integer primary key, idFile integer%s)", columns.c_str()));
3342       m_pDS->exec(PrepareSQL("INSERT INTO episodenew select idEpisode,idFile%s from episode", columnsSelect.c_str()));
3343       m_pDS->exec("DROP TABLE episode");
3344       m_pDS->exec("ALTER TABLE episodenew RENAME TO episode");
3345
3346       m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
3347       m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
3348       m_pDS->exec(PrepareSQL("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE));
3349       m_pDS->exec(PrepareSQL("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK));
3350
3351       //musicvideo
3352       m_pDS->exec(PrepareSQL("CREATE TABLE musicvideonew ( idMVideo integer primary key, idFile integer%s)", columns.c_str()));
3353       m_pDS->exec(PrepareSQL("INSERT INTO musicvideonew select idMVideo,idFile%s from musicvideo", columnsSelect.c_str()));
3354       m_pDS->exec("DROP TABLE musicvideo");
3355       m_pDS->exec("ALTER TABLE musicvideonew RENAME TO musicvideo");
3356
3357       m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
3358       m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
3359     }
3360     if (iVersion < 38)
3361     {
3362       m_pDS->exec("ALTER table movie add c21 text");
3363       m_pDS->exec("ALTER table episode add c21 text");
3364       m_pDS->exec("ALTER table musicvideo add c21 text");
3365       m_pDS->exec("ALTER table tvshow add c21 text");
3366
3367       CLog::Log(LOGINFO, "create country table");
3368       m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
3369
3370       CLog::Log(LOGINFO, "create countrylinkmovie table");
3371       m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
3372       m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
3373       m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
3374     }
3375     if (iVersion < 39)
3376     { // update for old scrapers
3377       m_pDS->query("select idPath,strScraper from path");
3378       set<CStdString> scrapers;
3379       while (!m_pDS->eof())
3380       {
3381         // translate the addon
3382         CStdString scraperID = ADDON::UpdateVideoScraper(m_pDS->fv(1).get_asString());
3383         if (!scraperID.IsEmpty())
3384         {
3385           scrapers.insert(scraperID);
3386           CStdString update = PrepareSQL("update path set strScraper='%s' where idPath=%i", scraperID.c_str(), m_pDS->fv(0).get_asInt());
3387           m_pDS2->exec(update);
3388         }
3389         m_pDS->next();
3390       }
3391       m_pDS->close();
3392       // ensure these scrapers are installed
3393       CAddonInstaller::Get().InstallFromXBMCRepo(scrapers);
3394     }
3395     if (iVersion < 40)
3396     {
3397       m_pDS->exec("DELETE FROM streamdetails");
3398       m_pDS->exec("ALTER table streamdetails add iVideoDuration integer");
3399     }
3400     if (iVersion < 41)
3401     {
3402       m_pDS->exec("ALTER table settings add PostProcess bool");
3403     }
3404     if (iVersion < 42)
3405     {
3406       m_pDS->exec("DELETE FROM streamdetails"); //Roll the stream details as changed from minutes to seconds
3407     }
3408     if (iVersion < 43)
3409     {
3410       m_pDS->exec("ALTER table settings add VerticalShift float");
3411     }
3412     if (iVersion < 44)
3413     {
3414       // only if MySQL is used and default character set is not utf8
3415       // string data needs to be converted to proper utf8
3416       CStdString charset = m_pDS->getDatabase()->getDefaultCharset();
3417       if (!m_sqlite && !charset.empty() && charset != "utf8")
3418       {
3419         map<CStdString, CStdStringArray> tables;
3420         map<CStdString, CStdStringArray>::iterator itt;
3421         CStdStringArray::iterator itc;
3422
3423         // columns that need to be converted
3424         // content columns
3425         CStdStringArray c_columns;
3426         for (int i = 0; i < 22; i++)
3427         {
3428           CStdString c;
3429           c.Format("c%02d", i);
3430           c_columns.push_back(c);
3431         }
3432
3433         tables.insert(pair<CStdString, CStdStringArray> ("episode", c_columns));
3434         tables.insert(pair<CStdString, CStdStringArray> ("movie", c_columns));
3435         tables.insert(pair<CStdString, CStdStringArray> ("musicvideo", c_columns));
3436         tables.insert(pair<CStdString, CStdStringArray> ("tvshow", c_columns));
3437
3438         //common columns
3439         CStdStringArray c1;
3440         c1.push_back("strRole");
3441         tables.insert(pair<CStdString, CStdStringArray> ("actorlinkepisode", c1));
3442         tables.insert(pair<CStdString, CStdStringArray> ("actorlinkmovie", c1));
3443         tables.insert(pair<CStdString, CStdStringArray> ("actorlinktvshow", c1));
3444
3445         //remaining columns
3446         CStdStringArray c2;
3447         c2.push_back("strActor");
3448         tables.insert(pair<CStdString, CStdStringArray> ("actors", c2));
3449
3450         CStdStringArray c3;
3451         c3.push_back("strCountry");
3452         tables.insert(pair<CStdString, CStdStringArray> ("country", c3));
3453
3454         CStdStringArray c4;
3455         c4.push_back("strFilename");
3456         tables.insert(pair<CStdString, CStdStringArray> ("files", c4));
3457
3458         CStdStringArray c5;
3459         c5.push_back("strGenre");
3460         tables.insert(pair<CStdString, CStdStringArray> ("genre", c5));
3461
3462         CStdStringArray c6;
3463         c6.push_back("strSet");
3464         tables.insert(pair<CStdString, CStdStringArray> ("sets", c6));
3465
3466         CStdStringArray c7;
3467         c7.push_back("strStudio");
3468         tables.insert(pair<CStdString, CStdStringArray> ("studio", c7));
3469
3470         for (itt = tables.begin(); itt != tables.end(); ++itt)
3471         {
3472           CStdString q;
3473           q = PrepareSQL("UPDATE `%s` SET", itt->first.c_str());
3474           for (itc = itt->second.begin(); itc != itt->second.end(); ++itc)
3475           {
3476             q += PrepareSQL(" `%s` = CONVERT(CAST(CONVERT(`%s` USING %s) AS BINARY) USING utf8)",
3477                             itc->c_str(), itc->c_str(), charset.c_str());
3478             if (*itc != itt->second.back())
3479             {
3480               q += ",";
3481             }
3482           }
3483           m_pDS->exec(q);
3484         }
3485       }
3486     }
3487     if (iVersion < 45)
3488     {
3489       m_pDS->exec("ALTER table movie add c22 text");
3490       m_pDS->exec("ALTER table episode add c22 text");
3491       m_pDS->exec("ALTER table musicvideo add c22 text");
3492       m_pDS->exec("ALTER table tvshow add c22 text");
3493       // Now update our tables
3494       UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH);
3495       UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH);
3496       UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH);
3497       UpdateBasePath("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, true);
3498     }
3499     if (iVersion < 46)
3500     { // add indices for dir entry lookups
3501       m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c22(255) )");
3502       m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c13(255) )");
3503       m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c18(255) )");
3504       m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c16(255) )");
3505     }
3506     if(iVersion < 49)
3507     {
3508       CreateViews();
3509     }
3510     if (iVersion < 50)
3511     {
3512       m_pDS->exec("ALTER table settings add ScalingMethod integer");
3513       m_pDS->exec(PrepareSQL("UPDATE settings set ScalingMethod=%i", g_settings.m_defaultVideoSettings.m_ScalingMethod));
3514     }
3515     if (iVersion < 51)
3516     {
3517       // Add iOrder fields to actorlink* tables to be able to list
3518       // actors by importance
3519       m_pDS->exec("ALTER TABLE actorlinkmovie ADD iOrder integer");
3520       m_pDS->exec("ALTER TABLE actorlinktvshow ADD iOrder integer");
3521       m_pDS->exec("ALTER TABLE actorlinkepisode ADD iOrder integer");
3522     }
3523   }
3524   catch (...)
3525   {
3526     CLog::Log(LOGERROR, "Error attempting to update the database version!");
3527     RollbackTransaction();
3528     return false;
3529   }
3530   CommitTransaction();
3531   return true;
3532 }
3533
3534 void CVideoDatabase::UpdateBasePath(const char *table, const char *id, int column, bool shows)
3535 {
3536   CStdString query;
3537   if (shows)
3538     query = PrepareSQL("SELECT idShow,path.strPath from tvshowlinkpath join path on tvshowlinkpath.idPath=path.idPath");
3539   else
3540     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);
3541
3542   map<CStdString, bool> paths;
3543   m_pDS2->query(query.c_str());
3544   while (!m_pDS2->eof())
3545   {
3546     CStdString path(m_pDS2->fv(1).get_asString());
3547     map<CStdString, bool>::iterator i = paths.find(path);
3548     if (i == paths.end())
3549     {
3550       SScanSettings settings;
3551       bool foundDirectly = false;
3552       ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
3553       if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
3554         paths.insert(make_pair(path, false)); // episodes
3555       else
3556         paths.insert(make_pair(path, settings.parent_name_root)); // shows, movies, musicvids
3557       i = paths.find(path);
3558     }
3559     CStdString filename;
3560     if (!shows)
3561       ConstructPath(filename, path, m_pDS2->fv(2).get_asString());
3562     else
3563       filename = path;
3564     CFileItem item(filename, shows);
3565     path = item.GetBaseMoviePath(i->second);
3566     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());
3567     m_pDS->exec(sql.c_str());
3568     m_pDS2->next();
3569   }
3570   m_pDS2->close();
3571 }
3572
3573 int CVideoDatabase::GetPlayCount(const CFileItem &item)
3574 {
3575   int id = GetFileId(item);
3576   if (id < 0)
3577     return 0;  // not in db, so not watched
3578
3579   try
3580   {
3581     // error!
3582     if (NULL == m_pDB.get()) return -1;
3583     if (NULL == m_pDS.get()) return -1;
3584
3585     CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
3586     int count = 0;
3587     if (m_pDS->query(strSQL.c_str()))
3588     {
3589       // there should only ever be one row returned
3590       if (m_pDS->num_rows() == 1)
3591         count = m_pDS->fv(0).get_asInt();
3592       m_pDS->close();
3593     }
3594     return count;
3595   }
3596   catch (...)
3597   {
3598     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3599   }
3600   return -1;
3601 }
3602
3603 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
3604 {
3605   if (NULL == m_pDB.get()) return;
3606   if (NULL == m_pDS.get()) return;
3607   if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
3608
3609   CStdString exec;
3610   if (type == VIDEODB_CONTENT_TVSHOWS)
3611     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);
3612   else if (type == VIDEODB_CONTENT_MOVIES)
3613     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);
3614
3615   try
3616   {
3617     m_pDS->exec(exec.c_str());
3618
3619     if (type == VIDEODB_CONTENT_TVSHOWS)
3620       AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
3621     else if (type == VIDEODB_CONTENT_MOVIES)
3622       AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
3623   }
3624   catch (...)
3625   {
3626     CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.m_strPath.c_str());
3627   }
3628 }
3629
3630 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CStdString &date)
3631 {
3632   int id = AddFile(item);
3633   if (id < 0)
3634     return;
3635
3636   // and mark as watched
3637   try
3638   {
3639     if (NULL == m_pDB.get()) return ;
3640     if (NULL == m_pDS.get()) return ;
3641
3642     CStdString strSQL;
3643     if (count)
3644     {
3645       if (date.IsEmpty())
3646         strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
3647       else
3648         strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.c_str(), id);
3649     }
3650     else
3651     {
3652       if (date.IsEmpty())
3653         strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
3654       else
3655         strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.c_str(), id);
3656     }
3657
3658     m_pDS->exec(strSQL.c_str());
3659
3660     CVariant data;
3661     data["playcount"] = count;
3662     ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Library, "xbmc", "NewPlayCount", CFileItemPtr(new CFileItem(item)), data);
3663   }
3664   catch (...)
3665   {
3666     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3667   }
3668 }
3669
3670 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
3671 {
3672   SetPlayCount(item, GetPlayCount(item) + 1);
3673 }
3674
3675 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
3676 {
3677   SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime().GetAsDBDateTime());
3678 }
3679
3680 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
3681 {
3682   try
3683   {
3684     if (NULL == m_pDB.get()) return ;
3685     if (NULL == m_pDS.get()) return ;
3686     CStdString content;
3687     CStdString strSQL;
3688     if (iType == VIDEODB_CONTENT_MOVIES)
3689     {
3690       CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3691       strSQL = PrepareSQL("UPDATE movie SET c%02d='%s' WHERE idMovie=%i", VIDEODB_ID_TITLE, strNewMovieTitle.c_str(), idMovie );
3692       content = "movie";
3693     }
3694     else if (iType == VIDEODB_CONTENT_EPISODES)
3695     {
3696       CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3697       strSQL = PrepareSQL("UPDATE episode SET c%02d='%s' WHERE idEpisode=%i", VIDEODB_ID_EPISODE_TITLE, strNewMovieTitle.c_str(), idMovie );
3698       content = "episode";
3699     }
3700     else if (iType == VIDEODB_CONTENT_TVSHOWS)
3701     {
3702       CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3703       strSQL = PrepareSQL("UPDATE tvshow SET c%02d='%s' WHERE idShow=%i", VIDEODB_ID_TV_TITLE, strNewMovieTitle.c_str(), idMovie );
3704       content = "tvshow";
3705     }
3706     else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
3707     {
3708       CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3709       strSQL = PrepareSQL("UPDATE musicvideo SET c%02d='%s' WHERE idMVideo=%i", VIDEODB_ID_MUSICVIDEO_TITLE, strNewMovieTitle.c_str(), idMovie );
3710       content = "musicvideo";
3711     }
3712     else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
3713     {
3714       CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3715       strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
3716     }
3717     m_pDS->exec(strSQL.c_str());
3718
3719     if (content.size() > 0)
3720       AnnounceUpdate(content, idMovie);
3721   }
3722   catch (...)
3723   {
3724     CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
3725   }
3726 }
3727
3728 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
3729 void CVideoDatabase::EraseVideoSettings()
3730 {
3731   try
3732   {
3733     CLog::Log(LOGINFO, "Deleting settings information for all movies");
3734     m_pDS->exec("delete from settings");
3735   }
3736   catch (...)
3737   {
3738     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3739   }
3740 }
3741
3742 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
3743 {
3744   return GetNavCommon(strBaseDir, items, "genre", idContent);
3745 }
3746
3747 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
3748 {
3749   return GetNavCommon(strBaseDir, items, "country", idContent);
3750 }
3751
3752 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
3753 {
3754   return GetNavCommon(strBaseDir, items, "studio", idContent);
3755 }
3756
3757 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent)
3758 {
3759   try
3760   {
3761     if (NULL == m_pDB.get()) return false;
3762     if (NULL == m_pDS.get()) return false;
3763
3764     CStdString strSQL;
3765     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
3766     {
3767       if (idContent == VIDEODB_CONTENT_MOVIES)
3768         strSQL = PrepareSQL("select %s.id%s,%s.str%s,path.strPath,files.playCount from %s join %slinkmovie on %s.id%s=%slinkmovie.id%s join movie on %slinkmovie.idMovie = movie.idMovie join files on files.idFile=movie.idFile join path on path.idPath=files.idPath",
3769                            type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
3770       else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
3771         strSQL = PrepareSQL("select %s.id%s,%s.str%s,path.strPath from %s join %slinktvshow on %s.id%s=%slinktvshow.id%s join tvshow on %slinktvshow.idShow=tvshow.idShow join tvshowlinkepisode on tvshowlinkepisode.idShow=tvshow.idShow join episode on episode.idEpisode=tvshowlinkepisode.idEpisode join files on files.idFile=episode.idFile join path on path.idPath=files.idPath",
3772                            type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
3773       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3774         strSQL = PrepareSQL("select %s.id%s,%s.str%s,path.strPath,files.playCount from %s join %slinkmusicvideo on %s.id%s=%slinkmusicvideo.id%s join musicvideo on %slinkmusicvideo.idMVideo = musicvideo.idMVideo join files on files.idFile=musicvideo.idFile join path on path.idPath=files.idPath",
3775                            type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
3776     }
3777     else
3778     {
3779       if (idContent == VIDEODB_CONTENT_MOVIES)
3780         strSQL = PrepareSQL("select %s.id%s,%s.str%s,count(1),count(files.playCount) from %s join %slinkmovie on %s.id%s=%slinkmovie.id%s join movie on %slinkmovie.idMovie=movie.idMovie join files on files.idFile=movie.idFile group by %s.id%s",
3781                            type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
3782       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
3783         strSQL = PrepareSQL("select distinct %s.id%s,%s.str%s from %s join %slinktvshow on %s.id%s=%slinktvshow.id%s join tvshow on %slinktvshow.idShow=tvshow.idShow",
3784                            type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
3785       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3786         strSQL = PrepareSQL("select %s.id%s,%s.str%s,count(1),count(files.playCount) from %s join %slinkmusicvideo on %s.id%s=%slinkmusicvideo.id%s join musicvideo on %slinkmusicvideo.idMVideo=musicvideo.idMVideo join files on files.idFile=musicvideo.idFile group by %s.id%s",
3787                            type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str());
3788     }
3789
3790     int iRowsFound = RunQuery(strSQL);
3791     if (iRowsFound <= 0)
3792       return iRowsFound == 0;
3793
3794     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
3795     {
3796       map<int, pair<CStdString,int> > mapItems;
3797       map<int, pair<CStdString,int> >::iterator it;
3798       while (!m_pDS->eof())
3799       {
3800         int id = m_pDS->fv(0).get_asInt();
3801         CStdString str = m_pDS->fv(1).get_asString();
3802
3803         // was this already found?
3804         it = mapItems.find(id);
3805         if (it == mapItems.end())
3806         {
3807           // check path
3808           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),g_settings.m_videoSources))
3809           {
3810             if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3811               mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
3812             else if (idContent == VIDEODB_CONTENT_TVSHOWS)
3813               mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
3814           }
3815         }
3816         m_pDS->next();
3817       }
3818       m_pDS->close();
3819
3820       for (it = mapItems.begin(); it != mapItems.end(); ++it)
3821       {
3822         CFileItemPtr pItem(new CFileItem(it->second.first));
3823         CStdString strDir;
3824         strDir.Format("%ld/", it->first);
3825         pItem->m_strPath = strBaseDir + strDir;
3826         pItem->m_bIsFolder = true;
3827         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3828           pItem->GetVideoInfoTag()->m_playCount = it->second.second;
3829         if (!items.Contains(pItem->m_strPath))
3830         {
3831           pItem->SetLabelPreformated(true);
3832           items.Add(pItem);
3833         }
3834       }
3835     }
3836     else
3837     {
3838       while (!m_pDS->eof())
3839       {
3840         CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
3841         CStdString strDir;
3842         strDir.Format("%ld/", m_pDS->fv(0).get_asInt());
3843         pItem->m_strPath = strBaseDir + strDir;
3844         pItem->m_bIsFolder = true;
3845         pItem->SetLabelPreformated(true);
3846         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3847         { // fv(3) is the number of videos watched, fv(2) is the total number.  We set the playcount
3848           // only if the number of videos watched is equal to the total number (i.e. every video watched)
3849           pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
3850         }
3851         items.Add(pItem);
3852         m_pDS->next();
3853       }
3854       m_pDS->close();
3855     }
3856     return true;
3857   }
3858   catch (...)
3859   {
3860     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3861   }
3862   return false;
3863 }
3864
3865 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent, const CStdString &where)
3866 {
3867   try
3868   {
3869     if (NULL == m_pDB.get()) return false;
3870     if (NULL == m_pDS.get()) return false;
3871
3872     // get primary sets for movies
3873     CStdString strSQL;
3874     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
3875     {
3876       if (idContent == VIDEODB_CONTENT_MOVIES)
3877         strSQL=PrepareSQL("SELECT sets.idSet,sets.strSet,path.strPath,files.playCount FROM sets JOIN setlinkmovie ON sets.idSet=setlinkmovie.idSet JOIN (SELECT idSet, COUNT(1) AS c FROM setlinkmovie GROUP BY idSet HAVING c>1) s2 ON s2.idSet=sets.idSet JOIN movie ON setlinkmovie.idMovie=movie.idMovie JOIN files ON files.idFile=movie.idFile JOIN path ON path.idPath=files.idPath");
3878       strSQL += where;
3879     }
3880     else
3881     {
3882       CStdString group;
3883       if (idContent == VIDEODB_CONTENT_MOVIES)
3884       {
3885         strSQL=PrepareSQL("SELECT sets.idSet,sets.strSet,COUNT(1) AS c,count(files.playCount) FROM sets JOIN setlinkmovie ON sets.idSet=setlinkmovie.idSet JOIN movie ON setlinkmovie.idMovie=movie.idMovie JOIN files ON files.idFile=movie.idFile ");
3886         group = " GROUP BY sets.idSet HAVING c>1";
3887       }
3888       strSQL += where;
3889       strSQL += group;
3890     }
3891
3892     int iRowsFound = RunQuery(strSQL);
3893     if (iRowsFound <= 0)
3894       return iRowsFound == 0;
3895
3896     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
3897     {
3898       map<int, pair<CStdString,int> > mapSets;
3899       map<int, pair<CStdString,int> >::iterator it;
3900       while (!m_pDS->eof())
3901       {
3902         int idSet = m_pDS->fv("sets.idSet").get_asInt();
3903         CStdString strSet = m_pDS->fv("sets.strSet").get_asString();
3904         it = mapSets.find(idSet);
3905         // was this set already found?
3906         if (it == mapSets.end())
3907         {
3908           // check path
3909           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
3910           {
3911             if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3912               mapSets.insert(pair<int, pair<CStdString,int> >(idSet, pair<CStdString,int>(strSet,m_pDS->fv(3).get_asInt())));
3913             else
3914               mapSets.insert(pair<int, pair<CStdString,int> >(idSet, pair<CStdString,int>(strSet,0)));
3915           }
3916         }
3917         m_pDS->next();
3918       }
3919       m_pDS->close();
3920
3921       for (it=mapSets.begin();it != mapSets.end();++it)
3922       {
3923         CFileItemPtr pItem(new CFileItem(it->second.first));
3924         CStdString strDir;
3925         strDir.Format("%ld/", it->first);
3926         pItem->m_strPath=strBaseDir + strDir;
3927         pItem->m_bIsFolder=true;
3928         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3929         {
3930           pItem->GetVideoInfoTag()->m_playCount = it->second.second;
3931           pItem->GetVideoInfoTag()->m_strTitle = pItem->GetLabel();
3932         }
3933         if (!items.Contains(pItem->m_strPath))
3934         {
3935           pItem->SetLabelPreformated(true);
3936           items.Add(pItem);
3937         }
3938       }
3939     }
3940     else
3941     {
3942       while (!m_pDS->eof())
3943       {
3944         CFileItemPtr pItem(new CFileItem(m_pDS->fv("sets.strSet").get_asString()));
3945         CStdString strDir;
3946         strDir.Format("%ld/", m_pDS->fv("sets.idSet").get_asInt());
3947         pItem->m_strPath=strBaseDir + strDir;
3948         pItem->m_bIsFolder=true;
3949         pItem->SetLabelPreformated(true);
3950         if (idContent == VIDEODB_CONTENT_MOVIES || idContent==VIDEODB_CONTENT_MUSICVIDEOS)
3951         {
3952           // fv(3) is the number of videos watched, fv(2) is the total number.  We set the playcount
3953           // only if the number of videos watched is equal to the total number (i.e. every video watched)
3954           pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
3955           pItem->GetVideoInfoTag()->m_strTitle = pItem->GetLabel();
3956         }
3957         bool thumb=false,fanart=false;
3958         if (CFile::Exists(pItem->GetCachedVideoThumb()))
3959         {
3960           pItem->SetThumbnailImage(pItem->GetCachedVideoThumb());
3961           thumb = true;
3962         }
3963         if (CFile::Exists(pItem->GetCachedFanart()))
3964         {
3965           pItem->SetProperty("fanart_image",pItem->GetCachedFanart());
3966           fanart = true;
3967         }
3968         if (!thumb || !fanart) // use the first item's thumb
3969         {
3970           CStdString strSQL = PrepareSQL("select strPath, strFileName from movieview join setlinkmovie on setlinkmovie.idMovie=movieview.idMovie where setlinkmovie.idSet=%u", m_pDS->fv("sets.idSet").get_asInt());
3971           m_pDS2->query(strSQL.c_str());
3972           if (!m_pDS2->eof())
3973           {
3974             CStdString path;
3975             ConstructPath(path,m_pDS2->fv(0).get_asString(),m_pDS2->fv(1).get_asString());
3976             CFileItem item(path,false);
3977             if (!thumb && CFile::Exists(item.GetCachedVideoThumb()))
3978               pItem->SetThumbnailImage(item.GetCachedVideoThumb());
3979             if (!fanart && CFile::Exists(item.GetCachedFanart()))
3980               pItem->SetProperty("fanart_image",item.GetCachedFanart());
3981             m_pDS2->close();
3982           }
3983         }
3984         items.Add(pItem);
3985         m_pDS->next();
3986       }
3987       m_pDS->close();
3988     }
3989
3990 //    CLog::Log(LOGDEBUG, "%s Time: %d ms", CTimeUtils::GetTimeMS() - time);
3991     return true;
3992   }
3993   catch (...)
3994   {
3995     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3996   }
3997   return false;
3998 }
3999
4000 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist)
4001 {
4002   try
4003   {
4004     if (NULL == m_pDB.get()) return false;
4005     if (NULL == m_pDS.get()) return false;
4006
4007     CStdString strSQL;
4008     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4009     {
4010       strSQL=PrepareSQL("select musicvideo.c%02d,musicvideo.idMVideo,actors.strActor,path.strPath from musicvideo join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideo.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist join files on files.idFile=musicvideo.idFile join path on path.idPath=files.idPath",VIDEODB_ID_MUSICVIDEO_ALBUM);
4011     }
4012     else
4013     {
4014       strSQL=PrepareSQL("select musicvideo.c%02d,musicvideo.idMVideo,actors.strActor from musicvideo join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideo.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist",VIDEODB_ID_MUSICVIDEO_ALBUM);
4015     }
4016     if (idArtist > -1)
4017       strSQL += PrepareSQL(" where artistlinkmusicvideo.idArtist=%i",idArtist);
4018
4019     strSQL += PrepareSQL(" group by musicvideo.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
4020
4021     int iRowsFound = RunQuery(strSQL);
4022     if (iRowsFound <= 0)
4023       return iRowsFound == 0;
4024
4025     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4026     {
4027       map<int, pair<CStdString,CStdString> > mapAlbums;
4028       map<int, pair<CStdString,CStdString> >::iterator it;
4029       while (!m_pDS->eof())
4030       {
4031         int lidMVideo = m_pDS->fv(1).get_asInt();
4032         CStdString strAlbum = m_pDS->fv(0).get_asString();
4033         it = mapAlbums.find(lidMVideo);
4034         // was this genre already found?
4035         if (it == mapAlbums.end())
4036         {
4037           // check path
4038           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
4039             mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
4040         }
4041         m_pDS->next();
4042       }
4043       m_pDS->close();
4044
4045       for (it=mapAlbums.begin();it != mapAlbums.end();++it)
4046       {
4047         if (!it->second.first.IsEmpty())
4048         {
4049           CFileItemPtr pItem(new CFileItem(it->second.first));
4050           CStdString strDir;
4051           strDir.Format("%ld/", it->first);
4052           pItem->m_strPath=strBaseDir + strDir;
4053           pItem->m_bIsFolder=true;
4054           pItem->SetLabelPreformated(true);
4055           if (!items.Contains(pItem->m_strPath))
4056           {
4057             pItem->GetVideoInfoTag()->m_strArtist = m_pDS->fv(2).get_asString();
4058             CStdString strThumb = CUtil::GetCachedAlbumThumb(pItem->GetLabel(),pItem->GetVideoInfoTag()->m_strArtist);
4059             if (CFile::Exists(strThumb))
4060               pItem->SetThumbnailImage(strThumb);
4061             items.Add(pItem);
4062           }
4063         }
4064       }
4065     }
4066     else
4067     {
4068       while (!m_pDS->eof())
4069       {
4070         if (!m_pDS->fv(0).get_asString().empty())
4071         {
4072           CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
4073           CStdString strDir;
4074           strDir.Format("%ld/", m_pDS->fv(1).get_asInt());
4075           pItem->m_strPath=strBaseDir + strDir;
4076           pItem->m_bIsFolder=true;
4077           pItem->SetLabelPreformated(true);
4078           if (!items.Contains(pItem->m_strPath))
4079           {
4080             pItem->GetVideoInfoTag()->m_strArtist = m_pDS->fv(2).get_asString();
4081             CStdString strThumb = CUtil::GetCachedAlbumThumb(pItem->GetLabel(),m_pDS->fv(2).get_asString());
4082             if (CFile::Exists(strThumb))
4083               pItem->SetThumbnailImage(strThumb);
4084             items.Add(pItem);
4085           }
4086         }
4087         m_pDS->next();
4088       }
4089       m_pDS->close();
4090     }
4091
4092 //    CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", CTimeUtils::GetTimeMS() - time);
4093     return true;
4094   }
4095   catch (...)
4096   {
4097     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4098   }
4099   return false;
4100 }
4101
4102 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
4103 {
4104   return GetPeopleNav(strBaseDir, items, "studio", idContent);
4105 }
4106
4107 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
4108 {
4109   return GetPeopleNav(strBaseDir, items, "director", idContent);
4110 }
4111
4112 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
4113 {
4114   if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent))
4115   { // set thumbs - ideally this should be in the normal thumb setting routines
4116     for (int i = 0; i < items.Size(); i++)
4117     {
4118       CFileItemPtr pItem = items[i];
4119       if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4120       {
4121         if (CFile::Exists(pItem->GetCachedArtistThumb()))
4122           pItem->SetThumbnailImage(pItem->GetCachedArtistThumb());
4123         pItem->SetIconImage("DefaultArtist.png");
4124       }
4125       else
4126       {
4127         if (CFile::Exists(pItem->GetCachedActorThumb()))
4128           pItem->SetThumbnailImage(pItem->GetCachedActorThumb());
4129         pItem->SetIconImage("DefaultActor.png");
4130       }
4131     }
4132     return true;
4133   }
4134   return false;
4135 }
4136
4137 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent)
4138 {
4139   if (NULL == m_pDB.get()) return false;
4140   if (NULL == m_pDS.get()) return false;
4141
4142   try
4143   {
4144     // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
4145     //       point.  This means that we *MUST* filter these levels as you'll get double ups.  Ideally we'd allow playcount
4146     //       to filter through as we normally do for tvshows to save this happening.
4147     //       Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
4148     //       Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
4149     //       above titles).
4150
4151     // General routine that the other actor/director/writer routines call
4152
4153     // get primary genres for movies
4154     CStdString strSQL;
4155     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4156     {
4157       if (idContent == VIDEODB_CONTENT_MOVIES)
4158         strSQL=PrepareSQL("select actors.idActor,actors.strActor,actors.strThumb,path.strPath,files.playCount from actors join %slinkmovie on actors.idActor=%slinkmovie.id%s join movie on %slinkmovie.idMovie=movie.idMovie join files on files.idFile=movie.idFile join path on path.idPath=files.idPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4159       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4160         strSQL=PrepareSQL("select actors.idActor,actors.strActor,actors.strThumb,path.strPath from actors join %slinktvshow on actors.idActor=%slinktvshow.id%s join tvshow on %slinktvshow.idShow = tvshow.idShow join tvshowlinkepisode on tvshowlinkepisode.idShow=tvshow.idShow join episode on episode.idEpisode=tvshowlinkepisode.idEpisode join files on files.idFile=episode.idFile join path on path.idPath = files.idPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4161       else if (idContent == VIDEODB_CONTENT_EPISODES)
4162         strSQL=PrepareSQL("select actors.idActor,actors.strActor,actors.strThumb,path.strPath,files.playCount from actors join %slinkepisode on actors.idActor=%slinkepisode.id%s join episode on %slinkepisode.idEpisode = episode.idEpisode join files on files.idFile=episode.idFile join path on path.idPath = files.idPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4163       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4164         strSQL=PrepareSQL("select actors.idActor,actors.strActor,actors.strThumb,path.strPath,files.playCount from actors join %slinkmusicvideo on actors.idActor=%slinkmusicvideo.id%s join musicvideo on %slinkmusicvideo.idMVideo = musicvideo.idMVideo join files on files.idFile=musicvideo.idFile join path on path.idPath = files.idPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4165     }
4166     else
4167     {
4168       if (idContent == VIDEODB_CONTENT_MOVIES)
4169       {
4170         strSQL=PrepareSQL("select actors.idActor,actors.strActor,actors.strThumb,count(1),count(files.playCount) from actors join %slinkmovie on actors.idActor=%slinkmovie.id%s join movie on %slinkmovie.idMovie=movie.idMovie join files on files.idFile=movie.idFile", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4171         strSQL += " group by actors.idActor";
4172       }
4173       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4174         strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor,actors.strThumb from actors,%slinktvshow,tvshow where actors.idActor=%slinktvshow.id%s and %slinktvshow.idShow=tvshow.idShow", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4175       else if (idContent == VIDEODB_CONTENT_EPISODES)
4176         strSQL=PrepareSQL("select actors.idActor,actors.strActor,actors.strThumb,count(1),count(files.playCount) from %slinkepisode,actors,episode,files where actors.idActor=%slinkepisode.id%s and %slinkepisode.idEpisode=episode.idEpisode and files.idFile=episode.idFile group by actors.idActor", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4177       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4178       {
4179         strSQL=PrepareSQL("select actors.idActor,actors.strActor,actors.strThumb,count(1),count(files.playCount) from actors join %slinkmusicvideo on actors.idActor=%slinkmusicvideo.id%s join musicvideo on %slinkmusicvideo.idMVideo=musicvideo.idMVideo join files on files.idFile=musicvideo.idFile", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4180         strSQL += " group by actors.idActor";
4181       }
4182     }
4183
4184     // run query
4185     unsigned int time = CTimeUtils::GetTimeMS();
4186     if (!m_pDS->query(strSQL.c_str())) return false;
4187     CLog::Log(LOGDEBUG, "%s -  query took %i ms",
4188               __FUNCTION__, CTimeUtils::GetTimeMS() - time); time = CTimeUtils::GetTimeMS();
4189     int iRowsFound = m_pDS->num_rows();
4190     if (iRowsFound == 0)
4191     {
4192       m_pDS->close();
4193       return true;
4194     }
4195
4196     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4197     {
4198       map<int, CActor> mapActors;
4199       map<int, CActor>::iterator it;
4200
4201       while (!m_pDS->eof())
4202       {
4203         int idActor = m_pDS->fv(0).get_asInt();
4204         CActor actor;
4205         actor.name = m_pDS->fv(1).get_asString();
4206         actor.thumb = m_pDS->fv(2).get_asString();
4207         if (idContent != VIDEODB_CONTENT_TVSHOWS)
4208           actor.playcount = m_pDS->fv(3).get_asInt();
4209         it = mapActors.find(idActor);
4210         // is this actor already known?
4211         if (it == mapActors.end())
4212         {
4213           // check path
4214           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
4215             mapActors.insert(pair<int, CActor>(idActor, actor));
4216         }
4217         m_pDS->next();
4218       }
4219       m_pDS->close();
4220
4221       for (it=mapActors.begin();it != mapActors.end();++it)
4222       {
4223         CFileItemPtr pItem(new CFileItem(it->second.name));
4224         CStdString strDir;
4225         strDir.Format("%ld/", it->first);
4226         pItem->m_strPath=strBaseDir + strDir;
4227         pItem->m_bIsFolder=true;
4228         pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
4229         pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
4230         items.Add(pItem);
4231       }
4232     }
4233     else
4234     {
4235       while (!m_pDS->eof())
4236       {
4237         try
4238         {
4239           CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4240           CStdString strDir;
4241           strDir.Format("%ld/", m_pDS->fv(0).get_asInt());
4242           pItem->m_strPath=strBaseDir + strDir;
4243           pItem->m_bIsFolder=true;
4244           pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
4245           if (idContent != VIDEODB_CONTENT_TVSHOWS)
4246           {
4247             // fv(4) is the number of videos watched, fv(3) is the total number.  We set the playcount
4248             // only if the number of videos watched is equal to the total number (i.e. every video watched)
4249             pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
4250           }
4251           if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4252             pItem->GetVideoInfoTag()->m_strArtist = pItem->GetLabel();
4253           items.Add(pItem);
4254           m_pDS->next();
4255         }
4256         catch (...)
4257         {
4258           m_pDS->close();
4259           CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
4260           return items.Size() > 0;
4261         }
4262       }
4263       m_pDS->close();
4264     }
4265     CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
4266               __FUNCTION__, CTimeUtils::GetTimeMS() - time); time = CTimeUtils::GetTimeMS();
4267
4268     return true;
4269   }
4270   catch (...)
4271   {
4272     m_pDS->close();
4273     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4274   }
4275   return false;
4276 }
4277
4278
4279
4280 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
4281 {
4282   try
4283   {
4284     if (NULL == m_pDB.get()) return false;
4285     if (NULL == m_pDS.get()) return false;
4286
4287     CStdString strSQL;
4288     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4289     {
4290       if (idContent == VIDEODB_CONTENT_MOVIES)
4291         strSQL = PrepareSQL("select movie.c%02d,path.strPath,files.playCount from movie join files on files.idFile=movie.idFile join path on files.idPath = path.idPath", VIDEODB_ID_YEAR);
4292       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4293         strSQL = PrepareSQL("select tvshow.c%02d,path.strPath from tvshow join files on files.idFile=episode.idFile join episode on episode.idEpisode=tvshowlinkepisode.idEpisode join tvshowlinkepisode on tvshow.idShow = tvshowlinkepisode.idShow join path on files.idPath = path.idPath", VIDEODB_ID_TV_PREMIERED);
4294       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4295         strSQL = PrepareSQL("select musicvideo.c%02d,path.strPath,files.playCount from musicvideo join files on files.idFile=musicvideo.idFile join path on files.idPath = path.idPath", VIDEODB_ID_MUSICVIDEO_YEAR);
4296     }
4297     else
4298     {
4299       CStdString group;
4300       if (idContent == VIDEODB_CONTENT_MOVIES)
4301       {
4302         strSQL = PrepareSQL("select movie.c%02d,count(1),count(files.playCount) from movie join files on files.idFile=movie.idFile", VIDEODB_ID_YEAR);
4303         group = PrepareSQL(" group by movie.c%02d", VIDEODB_ID_YEAR);
4304       }
4305       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4306         strSQL = PrepareSQL("select distinct tvshow.c%02d from tvshow", VIDEODB_ID_TV_PREMIERED);
4307       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4308       {
4309         strSQL = PrepareSQL("select musicvideo.c%02d,count(1),count(files.playCount) from musicvideo join files on files.idFile=musicvideo.idFile", VIDEODB_ID_MUSICVIDEO_YEAR);
4310         group = PrepareSQL(" group by musicvideo.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR);
4311       }
4312       strSQL += group;
4313     }
4314
4315     int iRowsFound = RunQuery(strSQL);
4316     if (iRowsFound <= 0)
4317       return iRowsFound == 0;
4318
4319     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4320     {
4321       map<int, pair<CStdString,int> > mapYears;
4322       map<int, pair<CStdString,int> >::iterator it;
4323       while (!m_pDS->eof())
4324       {
4325         int lYear = 0;
4326         if (idContent == VIDEODB_CONTENT_TVSHOWS)
4327         {
4328           CDateTime time;
4329           time.SetFromDateString(m_pDS->fv(0).get_asString());
4330           lYear = time.GetYear();
4331         }
4332         else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4333           lYear = m_pDS->fv(0).get_asInt();
4334         it = mapYears.find(lYear);
4335         if (it == mapYears.end())
4336         {
4337           // check path
4338           if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
4339           {
4340             CStdString year;
4341             year.Format("%d", lYear);
4342             if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4343               mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
4344             else
4345               mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
4346           }
4347         }
4348         m_pDS->next();
4349       }
4350       m_pDS->close();
4351
4352       for (it=mapYears.begin();it != mapYears.end();++it)
4353       {
4354         if (it->first == 0)
4355           continue;
4356         CFileItemPtr pItem(new CFileItem(it->second.first));
4357         CStdString strDir;
4358         strDir.Format("%ld/", it->first);
4359         pItem->m_strPath=strBaseDir + strDir;
4360         pItem->m_bIsFolder=true;
4361         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4362           pItem->GetVideoInfoTag()->m_playCount = it->second.second;
4363         items.Add(pItem);
4364       }
4365     }
4366     else
4367     {
4368       while (!m_pDS->eof())
4369       {
4370         int lYear = 0;
4371         CStdString strLabel;
4372         if (idContent == VIDEODB_CONTENT_TVSHOWS)
4373         {
4374           CDateTime time;
4375           time.SetFromDateString(m_pDS->fv(0).get_asString());
4376           lYear = time.GetYear();
4377           strLabel.Format("%i",lYear);
4378         }
4379         else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4380         {
4381           lYear = m_pDS->fv(0).get_asInt();
4382           strLabel = m_pDS->fv(0).get_asString();
4383         }
4384         if (lYear == 0)
4385         {
4386           m_pDS->next();
4387           continue;
4388         }
4389         CFileItemPtr pItem(new CFileItem(strLabel));
4390         CStdString strDir;
4391         strDir.Format("%ld/", lYear);
4392         pItem->m_strPath=strBaseDir + strDir;
4393         pItem->m_bIsFolder=true;
4394         if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4395         {
4396           // fv(2) is the number of videos watched, fv(1) is the total number.  We set the playcount
4397           // only if the number of videos watched is equal to the total number (i.e. every video watched)
4398           pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
4399         }
4400
4401         // take care of dupes ..
4402         if (!items.Contains(pItem->m_strPath))
4403           items.Add(pItem);
4404
4405         m_pDS->next();
4406       }
4407       m_pDS->close();
4408     }
4409
4410     return true;
4411   }
4412   catch (...)
4413   {
4414     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4415   }
4416   return false;
4417 }
4418
4419 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn)
4420 {
4421   try
4422   {
4423     if (NULL == m_pDB.get()) return false;
4424     if (NULL == m_pDS.get()) return false;
4425
4426     // look for duplicate show titles and stack them into a list
4427     if (idShow == -1)
4428       return false;
4429     CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
4430     CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
4431     if (!m_pDS->query(strSQL.c_str())) return false;
4432     int iRows = m_pDS->num_rows();
4433     if (iRows == 0) return false; // this should never happen!
4434     if (iRows > 1)
4435     { // more than one show, so stack them up
4436       strIn = "IN (";
4437       while (!m_pDS->eof())
4438       {
4439         strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
4440         m_pDS->next();
4441       }
4442       strIn[strIn.GetLength() - 1] = ')'; // replace last , with )
4443     }
4444     m_pDS->close();
4445     return true;
4446   }
4447   catch (...)
4448   {
4449     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4450   }
4451   return false;
4452 }
4453
4454 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow)
4455 {
4456   try
4457   {
4458     if (NULL == m_pDB.get()) return false;
4459     if (NULL == m_pDS.get()) return false;
4460
4461     CStdString strIn = PrepareSQL("= %i", idShow);
4462     GetStackedTvShowList(idShow, strIn);
4463
4464     CStdString strSQL = PrepareSQL("select episode.c%02d,path.strPath,tvshow.c%02d,tvshow.c%02d,tvshow.c%02d,tvshow.c%02d,count(1),count(files.playCount) from episode join tvshowlinkepisode on tvshowlinkepisode.idEpisode=episode.idEpisode join tvshow on tvshow.idShow=tvshowlinkepisode.idShow join files on files.idFile=episode.idFile ", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_GENRE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_MPAA);
4465     CStdString joins = PrepareSQL(" join tvshowlinkpath on tvshowlinkpath.idShow = tvshow.idShow join path on path.idPath = tvshowlinkpath.idPath where tvshow.idShow %s ", strIn.c_str());
4466     CStdString extraJoins, extraWhere;
4467     if (idActor != -1)
4468     {
4469       extraJoins = "join actorlinktvshow on actorlinktvshow.idShow=tvshow.idShow";
4470       extraWhere = PrepareSQL("and actorlinktvshow.idActor=%i", idActor);
4471     }
4472     else if (idDirector != -1)
4473     {
4474       extraJoins = "join directorlinktvshow on directorlinktvshow.idShow=tvshow.idShow";
4475       extraWhere = PrepareSQL("and directorlinktvshow.idDirector=%i",idDirector);
4476     }
4477     else if (idGenre != -1)
4478     {
4479       extraJoins = "join genrelinktvshow on genrelinktvshow.idShow=tvshow.idShow";
4480       extraWhere = PrepareSQL("and genrelinktvshow.idGenre=%i", idGenre);
4481     }
4482     else if (idYear != -1)
4483     {
4484       extraWhere = PrepareSQL("and tvshow.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, idYear);
4485     }
4486     strSQL += extraJoins + joins + extraWhere + PrepareSQL(" group by episode.c%02d", VIDEODB_ID_EPISODE_SEASON);
4487
4488     int iRowsFound = RunQuery(strSQL);
4489     if (iRowsFound <= 0)
4490       return iRowsFound == 0;
4491
4492     // show titles, studios and mpaa ratings will be the same
4493     CStdString showTitle = m_pDS->fv(2).get_asString();
4494     CStdString showStudio = m_pDS->fv(4).get_asString();
4495     CStdString showMPAARating = m_pDS->fv(5).get_asString();
4496
4497     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4498     {
4499       map<int, CSeason> mapSeasons;
4500       map<int, CSeason>::iterator it;
4501       while (!m_pDS->eof())
4502       {
4503         int iSeason = m_pDS->fv(0).get_asInt();
4504         it = mapSeasons.find(iSeason);
4505         // check path
4506         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
4507         {
4508           m_pDS->next();
4509           continue;
4510         }
4511         if (it == mapSeasons.end())
4512         {
4513           CSeason season;
4514           season.path = m_pDS->fv(1).get_asString();
4515           season.genre = m_pDS->fv(3).get_asString();
4516           season.numEpisodes = m_pDS->fv(6).get_asInt();
4517           season.numWatched = m_pDS->fv(7).get_asInt();
4518           mapSeasons.insert(make_pair(iSeason, season));
4519         }
4520         m_pDS->next();
4521       }
4522       m_pDS->close();
4523
4524       for (it=mapSeasons.begin();it != mapSeasons.end();++it)
4525       {
4526         int iSeason = it->first;
4527         CStdString strLabel;
4528         if (iSeason == 0)
4529           strLabel = g_localizeStrings.Get(20381);
4530         else
4531           strLabel.Format(g_localizeStrings.Get(20358),iSeason);
4532         CFileItemPtr pItem(new CFileItem(strLabel));
4533         CStdString strDir;
4534         strDir.Format("%ld/", it->first);
4535         pItem->m_strPath=strBaseDir + strDir;
4536         pItem->m_bIsFolder=true;
4537         pItem->GetVideoInfoTag()->m_strTitle = strLabel;
4538         pItem->GetVideoInfoTag()->m_iSeason = iSeason;
4539         pItem->GetVideoInfoTag()->m_iDbId = idShow;
4540         pItem->GetVideoInfoTag()->m_strPath = it->second.path;
4541         pItem->GetVideoInfoTag()->m_strGenre = it->second.genre;
4542         pItem->GetVideoInfoTag()->m_strStudio = showStudio;
4543         pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
4544         pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
4545         pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
4546         pItem->SetProperty("totalepisodes", it->second.numEpisodes);
4547         pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
4548         pItem->SetProperty("watchedepisodes", it->second.numWatched);
4549         pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
4550         pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
4551         pItem->SetCachedSeasonThumb();
4552         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
4553         items.Add(pItem);
4554       }
4555     }
4556     else
4557     {
4558       while (!m_pDS->eof())
4559       {
4560         int iSeason = m_pDS->fv(0).get_asInt();
4561         CStdString strLabel;
4562         if (iSeason == 0)
4563           strLabel = g_localizeStrings.Get(20381);
4564         else
4565           strLabel.Format(g_localizeStrings.Get(20358),iSeason);
4566         CFileItemPtr pItem(new CFileItem(strLabel));
4567         CStdString strDir;
4568         strDir.Format("%ld/", iSeason);
4569         pItem->m_strPath=strBaseDir + strDir;
4570         pItem->m_bIsFolder=true;
4571         pItem->GetVideoInfoTag()->m_strTitle = strLabel;
4572         pItem->GetVideoInfoTag()->m_iSeason = iSeason;
4573         pItem->GetVideoInfoTag()->m_iDbId = idShow;
4574         pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
4575         pItem->GetVideoInfoTag()->m_strGenre = m_pDS->fv(3).get_asString();
4576         pItem->GetVideoInfoTag()->m_strStudio = showStudio;
4577         pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
4578         pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
4579         int totalEpisodes = m_pDS->fv(6).get_asInt();
4580         int watchedEpisodes = m_pDS->fv(7).get_asInt();
4581         pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
4582         pItem->SetProperty("totalepisodes", totalEpisodes);
4583         pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
4584         pItem->SetProperty("watchedepisodes", watchedEpisodes);
4585         pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
4586         pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
4587         pItem->SetCachedSeasonThumb();
4588         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
4589         items.Add(pItem);
4590         m_pDS->next();
4591       }
4592       m_pDS->close();
4593     }
4594     // now add any linked movies
4595     CStdString where = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie where movielinktvshow.idShow %s", strIn.c_str());
4596     GetMoviesByWhere("videodb://1/2/", where, "", items);
4597     return true;
4598   }
4599   catch (...)
4600   {
4601     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4602   }
4603   return false;
4604 }
4605
4606 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idStudio, int idCountry, int idSet)
4607 {
4608   CStdString where;
4609   if (idGenre != -1)
4610     where = PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie=movieview.idMovie where genrelinkmovie.idGenre=%i", idGenre);
4611   else if (idCountry != -1)
4612     where = PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie=movieview.idMovie where countrylinkmovie.idCountry=%i", idCountry);
4613   else if (idStudio != -1)
4614     where = PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie=movieview.idMovie where studiolinkmovie.idStudio=%i", idStudio);
4615   else if (idDirector != -1)
4616     where = PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie=movieview.idMovie where directorlinkmovie.idDirector=%i", idDirector);
4617   else if (idYear !=-1)
4618     where = PrepareSQL("where c%02d='%i'",VIDEODB_ID_YEAR,idYear);
4619   else if (idActor != -1)
4620     where = PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie=movieview.idMovie join actors on actors.idActor=actorlinkmovie.idActor where actors.idActor=%i",idActor);
4621   else if (idSet != -1)
4622     where = PrepareSQL("join setlinkmovie on setlinkmovie.idMovie=movieview.idMovie where setlinkmovie.idSet=%u",idSet);
4623
4624   return GetMoviesByWhere(strBaseDir, where, "", items, idSet == -1);
4625 }
4626
4627 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const CStdString &where, const CStdString &order, CFileItemList& items, bool fetchSets)
4628 {
4629   try
4630   {
4631     movieTime = 0;
4632     castTime = 0;
4633
4634     if (NULL == m_pDB.get()) return false;
4635     if (NULL == m_pDS.get()) return false;
4636
4637     CStdString strSQL = "select * from movieview ";
4638     if (fetchSets && !g_guiSettings.GetBool("videolibrary.flattenmoviesets"))
4639     {
4640       // not getting a set, so grab all sets that match this where clause first
4641       CStdString setsWhere;
4642       if (where.size())
4643         setsWhere = " where movie.idMovie in (select movieview.idMovie from movieview " + where + ")";
4644       GetSetsNav("videodb://1/7/", items, VIDEODB_CONTENT_MOVIES, setsWhere);
4645       if (where.size())
4646         strSQL += where + PrepareSQL(" and movieview.idMovie NOT in (select idMovie from setlinkmovie)");
4647       else
4648         strSQL += PrepareSQL("WHERE movieview.idMovie NOT IN (SELECT idMovie FROM setlinkmovie s1 JOIN(SELECT idSet, COUNT(1) AS c FROM setlinkmovie GROUP BY idSet HAVING c>1) s2 ON s2.idSet=s1.idSet)");
4649     }
4650     else
4651       strSQL += where;
4652
4653     if (order.size())
4654       strSQL += " " + order;
4655
4656     int iRowsFound = RunQuery(strSQL);
4657     if (iRowsFound <= 0)
4658       return iRowsFound == 0;
4659
4660     // get data from returned rows
4661     items.Reserve(iRowsFound);
4662     while (!m_pDS->eof())
4663     {
4664       CVideoInfoTag movie = GetDetailsForMovie(m_pDS);
4665       if (g_settings.GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
4666           g_passwordManager.bMasterUser                                   ||
4667           g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, g_settings.m_videoSources))
4668       {
4669         CFileItemPtr pItem(new CFileItem(movie));
4670         pItem->m_strPath.Format("%s%ld", strBaseDir.c_str(), movie.m_iDbId);
4671         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
4672         items.Add(pItem);
4673       }
4674       m_pDS->next();
4675     }
4676
4677     // cleanup
4678     m_pDS->close();
4679     return true;
4680   }
4681   catch (...)
4682   {
4683     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4684   }
4685   return false;
4686 }
4687
4688 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idStudio)
4689 {
4690   CStdString where;
4691   if (idGenre != -1)
4692     where = PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow=tvshowview.idShow where genrelinktvshow.idGenre=%i ", idGenre);
4693   else if (idStudio != -1)
4694     where = PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow=tvshowview.idShow where studiolinktvshow.idStudio=%i", idStudio);
4695   else if (idDirector != -1)
4696     where = PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow=tvshowview.idShow where directorlinktvshow.idDirector=%i", idDirector);
4697   else if (idYear != -1)
4698     where = PrepareSQL("where c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED,idYear);
4699   else if (idActor != -1)
4700     where = PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow=tvshowview.idShow join actors on actors.idActor=actorlinktvshow.idActor where actors.idActor=%i",idActor);
4701
4702   return GetTvShowsByWhere(strBaseDir, where, items);
4703 }
4704
4705 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const CStdString &where, CFileItemList& items)
4706 {
4707   try
4708   {
4709     movieTime = 0;
4710
4711     if (NULL == m_pDB.get()) return false;
4712     if (NULL == m_pDS.get()) return false;
4713
4714     int iRowsFound = RunQuery("SELECT * FROM tvshowview " + where);
4715     if (iRowsFound <= 0)
4716       return iRowsFound == 0;
4717
4718     // get data from returned rows
4719     items.Reserve(iRowsFound);
4720
4721     while (!m_pDS->eof())
4722     {
4723       int idShow = m_pDS->fv("tvshow.idShow").get_asInt();
4724       int numSeasons = m_pDS->fv(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt();
4725
4726       CVideoInfoTag movie = GetDetailsForTvShow(m_pDS, false);
4727       if (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0)
4728       {
4729         CFileItemPtr pItem(new CFileItem(movie));
4730         pItem->m_strPath.Format("%s%ld/", strBaseDir.c_str(), idShow);
4731         pItem->m_dateTime.SetFromDateString(movie.m_strPremiered);
4732         pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
4733         pItem->SetProperty("totalseasons", numSeasons);
4734         pItem->SetProperty("totalepisodes", movie.m_iEpisode);
4735         pItem->SetProperty("numepisodes", movie.m_iEpisode); // will be changed later to reflect watchmode setting
4736         pItem->SetProperty("watchedepisodes", movie.m_playCount);
4737         pItem->SetProperty("unwatchedepisodes", movie.m_iEpisode - movie.m_playCount);
4738         pItem->GetVideoInfoTag()->m_playCount = (movie.m_iEpisode == movie.m_playCount) ? 1 : 0;
4739         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
4740         items.Add(pItem);
4741       }
4742       m_pDS->next();
4743     }
4744
4745     CStdString order(where);
4746     bool maintainOrder = order.ToLower().Find("order by") != -1;
4747     Stack(items, VIDEODB_CONTENT_TVSHOWS, maintainOrder);
4748
4749     // cleanup
4750     m_pDS->close();
4751     return true;
4752   }
4753   catch (...)
4754   {
4755     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4756   }
4757   return false;
4758 }
4759
4760 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
4761 {
4762   if (maintainSortOrder)
4763   {
4764     // save current sort order
4765     for (int i = 0; i < items.Size(); i++)
4766       items[i]->m_iprogramCount = i;
4767   }
4768
4769   switch (type)
4770   {
4771     case VIDEODB_CONTENT_TVSHOWS:
4772     {
4773       // sort by Title
4774       items.Sort(SORT_METHOD_VIDEO_TITLE, SORT_ORDER_ASC);
4775
4776       int i = 0;
4777       while (i < items.Size())
4778       {
4779         CFileItemPtr pItem = items.Get(i);
4780         CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
4781         CStdString strFanArt = pItem->GetProperty("fanart_image");
4782
4783         int j = i + 1;
4784         bool bStacked = false;
4785         while (j < items.Size())
4786         {
4787           CFileItemPtr jItem = items.Get(j);
4788
4789           // matching title? append information
4790           if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
4791           {
4792             if (jItem->GetVideoInfoTag()->m_strPremiered != 
4793                 pItem->GetVideoInfoTag()->m_strPremiered)
4794             {
4795               j++;
4796               continue;
4797             }
4798             bStacked = true;
4799
4800             // increment episode counts
4801             pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
4802             pItem->IncrementProperty("totalepisodes", jItem->GetPropertyInt("totalepisodes"));
4803             pItem->IncrementProperty("numepisodes", jItem->GetPropertyInt("numepisodes")); // will be changed later to reflect watchmode setting
4804             pItem->IncrementProperty("watchedepisodes", jItem->GetPropertyInt("watchedepisodes"));
4805             pItem->IncrementProperty("unwatchedepisodes", jItem->GetPropertyInt("unwatchedepisodes"));
4806
4807             // check for fanart if not already set
4808             if (strFanArt.IsEmpty())
4809               strFanArt = jItem->GetProperty("fanart_image");
4810
4811             // remove duplicate entry
4812             items.Remove(j);
4813           }
4814           // no match? exit loop
4815           else
4816             break;
4817         }
4818         // update playcount and fanart
4819         if (bStacked)
4820         {
4821           pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == pItem->GetPropertyInt("watchedepisodes")) ? 1 : 0;
4822           pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
4823           if (!strFanArt.IsEmpty())
4824             pItem->SetProperty("fanart_image", strFanArt);
4825         }
4826         // increment i to j which is the next item
4827         i = j;
4828       }
4829     }
4830     break;
4831     // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
4832     // so that if we eventually want to stack during scan we can utilize it.
4833     /*
4834     case VIDEODB_CONTENT_EPISODES:
4835     {
4836       // sort by ShowTitle, Episode, Filename
4837       items.Sort(SORT_METHOD_EPISODE, SORT_ORDER_ASC);
4838
4839       int i = 0;
4840       while (i < items.Size())
4841       {
4842         CFileItemPtr pItem = items.Get(i);
4843         CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
4844         int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
4845         int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
4846         //CStdString strFanArt = pItem->GetProperty("fanart_image");
4847
4848         // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
4849         CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
4850         bool bDvdFolder = strFileNameAndPath.Right(12).Equals("VIDEO_TS.IFO");
4851
4852         vector<CStdString> paths;
4853         paths.push_back(strFileNameAndPath);
4854         CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
4855
4856         int j = i + 1;
4857         int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
4858         while (j < items.Size())
4859         {
4860           CFileItemPtr jItem = items.Get(j);
4861           const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
4862           CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
4863
4864           CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
4865           // compare path, season, episode
4866           if (
4867             jTag &&
4868             jTag->m_strPath.Equals(strPath) &&
4869             jTag->m_iSeason == iSeason &&
4870             jTag->m_iEpisode == iEpisode
4871             )
4872           {
4873             // keep checking to see if this is dvd folder
4874             if (!bDvdFolder)
4875             {
4876               bDvdFolder = jFileNameAndPath.Right(12).Equals("VIDEO_TS.IFO");
4877               // if we have a dvd folder, we stack differently
4878               if (bDvdFolder)
4879               {
4880                 // remove all the other items and ONLY show the VIDEO_TS.IFO file
4881                 paths.empty();
4882                 paths.push_back(jFileNameAndPath);
4883               }
4884               else
4885               {
4886                 // increment playcount
4887                 iPlayCount += jTag->m_playCount;
4888
4889                 // episodes dont have fanart yet
4890                 //if (strFanArt.IsEmpty())
4891                 //  strFanArt = jItem->GetProperty("fanart_image");
4892
4893                 paths.push_back(jFileNameAndPath);
4894               }
4895             }
4896
4897             // remove duplicate entry
4898             jTag = NULL;
4899             items.Remove(j);
4900           }
4901           // no match? exit loop
4902           else
4903             break;
4904         }
4905         // update playcount and fanart if we have a stacked entry
4906         if (paths.size() > 1)
4907         {
4908           CStackDirectory dir;
4909           CStdString strStack;
4910           dir.ConstructStackPath(paths, strStack);
4911           pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
4912           pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
4913           pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
4914
4915           // episodes dont have fanart yet
4916           //if (!strFanArt.IsEmpty())
4917           //  pItem->SetProperty("fanart_image", strFanArt);
4918         }
4919         // increment i to j which is the next item
4920         i = j;
4921       }
4922     }
4923     break;*/
4924
4925     // stack other types later
4926     default:
4927       break;
4928   }
4929   if (maintainSortOrder)
4930   {
4931     // restore original sort order - essential for smartplaylists
4932     items.Sort(SORT_METHOD_PROGRAM_COUNT, SORT_ORDER_ASC);
4933   }
4934 }
4935
4936 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason)
4937 {
4938   CStdString strIn = PrepareSQL("= %i", idShow);
4939   GetStackedTvShowList(idShow, strIn);
4940   CStdString where = PrepareSQL("where idShow %s",strIn.c_str());
4941   if (idGenre != -1)
4942     where = PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow=episodeview.idShow where episodeview.idShow=%i and genrelinktvshow.idGenre=%i",idShow,idGenre);
4943   else if (idDirector != -1)
4944     where = PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow=episodeview.idShow where episodeview.idShow=%i and directorlinktvshow.idDirector=%i",idShow,idDirector);
4945   else if (idYear !=-1)
4946     where=PrepareSQL("where idShow=%i and premiered like '%%%i%%'",idShow,idYear);
4947   else if (idActor != -1)
4948     where = PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow=episodeview.idShow where episodeview.idShow=%i and actorlinktvshow.idActor=%i",idShow,idActor);
4949
4950   if (idSeason != -1)
4951   {
4952     if (idSeason != 0) // season = 0 indicates a special - we grab all specials here (see below)
4953       where += PrepareSQL(" and (c%02d=%i or (c%02d=0 and (c%02d=0 or c%02d=%i)))",VIDEODB_ID_EPISODE_SEASON,idSeason,VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON,idSeason);
4954     else
4955       where += PrepareSQL(" and c%02d=%i",VIDEODB_ID_EPISODE_SEASON,idSeason);
4956   }
4957
4958   // we always append show, season + episode in GetEpisodesByWhere
4959   CStdString parent, grandParent;
4960   URIUtils::GetParentPath(strBaseDir,parent);
4961   URIUtils::GetParentPath(parent,grandParent);
4962
4963   bool ret = GetEpisodesByWhere(grandParent, where, items);
4964
4965   if (idSeason == -1)
4966   { // add any linked movies
4967     CStdString where = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie where movielinktvshow.idShow %s", strIn.c_str());
4968     GetMoviesByWhere("videodb://1/2/", where, "", items);
4969   }
4970   return ret;
4971 }
4972
4973 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const CStdString &where, CFileItemList& items, bool appendFullShowPath /* = true */)
4974 {
4975   try
4976   {
4977     movieTime = 0;
4978     castTime = 0;
4979
4980     if (NULL == m_pDB.get()) return false;
4981     if (NULL == m_pDS.get()) return false;
4982
4983     int iRowsFound = RunQuery("select * from episodeview " + where);
4984     if (iRowsFound <= 0)
4985       return iRowsFound == 0;
4986
4987     // get data from returned rows
4988     items.Reserve(iRowsFound);
4989     while (!m_pDS->eof())
4990     {
4991       int idEpisode = m_pDS->fv("idEpisode").get_asInt();
4992       int idShow = m_pDS->fv("idShow").get_asInt();
4993
4994       CVideoInfoTag movie = GetDetailsForEpisode(m_pDS);
4995       CFileItemPtr pItem(new CFileItem(movie));
4996       if (appendFullShowPath)
4997         pItem->m_strPath.Format("%s%ld/%ld/%ld",strBaseDir.c_str(), idShow, movie.m_iSeason,idEpisode);
4998       else
4999         pItem->m_strPath.Format("%s%ld",strBaseDir.c_str(), idEpisode);
5000
5001       pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
5002       pItem->m_dateTime.SetFromDateString(movie.m_strFirstAired);
5003       pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
5004       items.Add(pItem);
5005
5006       m_pDS->next();
5007     }
5008
5009     // cleanup
5010     m_pDS->close();
5011     return true;
5012   }
5013   catch (...)
5014   {
5015     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5016   }
5017   return false;
5018 }
5019
5020
5021 bool CVideoDatabase::GetMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idArtist, int idDirector, int idStudio, int idAlbum)
5022 {
5023   CStdString where;
5024   if (idGenre != -1)
5025     where = PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo=musicvideoview.idMVideo where genrelinkmusicvideo.idGenre=%i", idGenre);
5026   else if (idStudio != -1)
5027     where = PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo=musicvideoview.idMVideo where studiolinkmusicvideo.idStudio=%i", idStudio);
5028   else if (idDirector != -1)
5029     where = PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo=musicvideoview.idMVideo where directorlinkmusicvideo.idDirector=%i", idDirector);
5030   else if (idYear !=-1)
5031     where = PrepareSQL("where c%02d='%i'",VIDEODB_ID_MUSICVIDEO_YEAR,idYear);
5032   else if (idArtist != -1)
5033     where = PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist where actors.idActor=%i",idArtist);
5034   if (idAlbum != -1)
5035    {
5036     CStdString str2 = PrepareSQL(" musicvideoview.c%02d=(select c%02d from musicvideo where idMVideo=%i)",VIDEODB_ID_MUSICVIDEO_ALBUM,VIDEODB_ID_MUSICVIDEO_ALBUM,idAlbum);
5037     if (where.IsEmpty())
5038       where.Format(" %s%s","where",str2.c_str());
5039     else
5040       where.Format(" %s %s%s",where.Mid(0).c_str(),"and",str2.c_str());
5041   }
5042
5043   return GetMusicVideosByWhere(strBaseDir, where, items);
5044 }
5045
5046 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items)
5047 {
5048   CStdString order = PrepareSQL("order by idMovie desc limit %i", g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
5049   return GetMoviesByWhere(strBaseDir, "", order, items);
5050 }
5051
5052 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items)
5053 {
5054   CStdString where = PrepareSQL("order by idEpisode desc limit %i", g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
5055   return GetEpisodesByWhere(strBaseDir, where, items, false);
5056 }
5057
5058 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items)
5059 {
5060   CStdString where = PrepareSQL("order by idMVideo desc limit %i", g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
5061   return GetMusicVideosByWhere(strBaseDir, where, items);
5062 }
5063
5064 CStdString CVideoDatabase::GetGenreById(int id)
5065 {
5066   return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
5067 }
5068
5069 CStdString CVideoDatabase::GetCountryById(int id)
5070 {
5071   return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
5072 }
5073
5074 CStdString CVideoDatabase::GetSetById(int id)
5075 {
5076   return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
5077 }
5078
5079 CStdString CVideoDatabase::GetPersonById(int id)
5080 {
5081   return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
5082 }
5083
5084 CStdString CVideoDatabase::GetStudioById(int id)
5085 {
5086   return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
5087 }
5088
5089 CStdString CVideoDatabase::GetTvShowTitleById(int id)
5090 {
5091   return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
5092 }
5093
5094 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
5095 {
5096   return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
5097 }
5098
5099 bool CVideoDatabase::HasSets() const
5100 {
5101   try
5102   {
5103     if (NULL == m_pDB.get()) return false;
5104     if (NULL == m_pDS.get()) return false;
5105
5106     m_pDS->query( "select idSet from sets" );
5107     bool bResult = (m_pDS->num_rows() > 0);
5108     m_pDS->close();
5109     return bResult;
5110   }
5111   catch (...)
5112   {
5113     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5114   }
5115   return false;
5116 }
5117
5118 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
5119 {
5120   try
5121   {
5122     if (NULL == m_pDB.get()) return false;
5123     if (NULL == m_pDS2.get()) return false;
5124
5125     // make sure we use m_pDS2, as this is called in loops using m_pDS
5126     CStdString strSQL=PrepareSQL("select idShow from tvshowlinkepisode where idEpisode=%i", idEpisode);
5127     m_pDS2->query( strSQL.c_str() );
5128
5129     int result=-1;
5130     if (!m_pDS2->eof())
5131       result=m_pDS2->fv(0).get_asInt();
5132     m_pDS2->close();
5133
5134     return result;
5135   }
5136   catch (...)
5137   {
5138     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
5139   }
5140   return false;
5141 }
5142
5143 bool CVideoDatabase::HasContent()
5144 {
5145   return (HasContent(VIDEODB_CONTENT_MOVIES) ||
5146           HasContent(VIDEODB_CONTENT_TVSHOWS) ||
5147           HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
5148 }
5149
5150 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
5151 {
5152   bool result = false;
5153   try
5154   {
5155     if (NULL == m_pDB.get()) return false;
5156     if (NULL == m_pDS.get()) return false;
5157
5158     CStdString sql;
5159     if (type == VIDEODB_CONTENT_MOVIES)
5160       sql = "select count(1) from movie";
5161     else if (type == VIDEODB_CONTENT_TVSHOWS)
5162       sql = "select count(1) from tvshow";
5163     else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
5164       sql = "select count(1) from musicvideo";
5165     m_pDS->query( sql.c_str() );
5166
5167     if (!m_pDS->eof())
5168       result = (m_pDS->fv(0).get_asInt() > 0);
5169
5170     m_pDS->close();
5171   }
5172   catch (...)
5173   {
5174     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5175   }
5176   return result;
5177 }
5178
5179 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
5180 {
5181   try
5182   {
5183     if (NULL == m_pDB.get()) return 0;
5184     if (NULL == m_pDS.get()) return 0;
5185
5186     CStdString strSQL;
5187     strSQL.Format("select count(1) as nummovies from musicvideoview %s",strWhere.c_str());
5188     m_pDS->query( strSQL.c_str() );
5189
5190     int iResult = 0;
5191     if (!m_pDS->eof())
5192       iResult = m_pDS->fv("nummovies").get_asInt();
5193
5194     m_pDS->close();
5195     return iResult;
5196   }
5197   catch (...)
5198   {
5199     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5200   }
5201   return 0;
5202 }
5203
5204 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
5205 {
5206   SScanSettings settings;
5207   return GetScraperForPath(strPath, settings);
5208 }
5209
5210 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
5211 {
5212   bool dummy;
5213   return GetScraperForPath(strPath, settings, dummy);
5214 }
5215
5216 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
5217 {
5218   foundDirectly = false;
5219   try
5220   {
5221     if (strPath.IsEmpty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
5222
5223     ScraperPtr scraper;
5224     CStdString strPath1;
5225     CStdString strPath2(strPath);
5226
5227     if (URIUtils::IsMultiPath(strPath))
5228       strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
5229
5230     URIUtils::GetDirectory(strPath2,strPath1);
5231     int idPath = GetPathId(strPath1);
5232
5233     if (idPath > -1)
5234     {
5235       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);
5236       m_pDS->query( strSQL.c_str() );
5237     }
5238
5239     int iFound = 1;
5240     CONTENT_TYPE content = CONTENT_NONE;
5241     if (!m_pDS->eof())
5242     { // path is stored in db
5243
5244       if (m_pDS->fv("path.exclude").get_asBool())
5245       {
5246         settings.exclude = true;
5247         m_pDS->close();
5248         return ScraperPtr();
5249       }
5250       settings.exclude = false;
5251
5252       // try and ascertain scraper for this path
5253       CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
5254       strcontent.ToLower();
5255       content = TranslateContent(strcontent);
5256
5257       //FIXME paths stored should not have empty strContent
5258       //assert(content != CONTENT_NONE);
5259       CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
5260
5261       AddonPtr addon;
5262       if (!scraperID.empty() &&
5263         CAddonMgr::Get().GetAddon(scraperID, addon))
5264       {
5265         scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone(addon));
5266         if (!scraper)
5267           return ScraperPtr();
5268
5269         // store this path's content & settings
5270         scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
5271         settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
5272         settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
5273         settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
5274       }
5275     }
5276
5277     if (content == CONTENT_NONE)
5278     { // this path is not saved in db
5279       // we must drill up until a scraper is configured
5280       CStdString strParent;
5281       while (URIUtils::GetParentPath(strPath1, strParent))
5282       {
5283         iFound++;
5284
5285         CStdString strSQL=PrepareSQL("select path.strContent,path.strScraper,path.scanRecursive,path.useFolderNames,path.strSettings,path.noUpdate from path where strPath like '%s'",strParent.c_str());
5286         m_pDS->query(strSQL.c_str());
5287
5288         CONTENT_TYPE content = CONTENT_NONE;
5289         if (!m_pDS->eof())
5290         {
5291
5292           CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
5293           strcontent.ToLower();
5294           if (strcontent.Equals("none"))
5295           {
5296             settings.exclude = true;
5297             scraper.reset();
5298             m_pDS->close();
5299             break;
5300           }
5301
5302           content = TranslateContent(strcontent);
5303
5304           AddonPtr addon;
5305           if (content != CONTENT_NONE &&
5306               CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
5307           {
5308             scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone(addon));
5309             scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
5310             settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
5311             settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
5312             settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
5313             settings.exclude = false;
5314             break;
5315           }
5316         }
5317         strPath1 = strParent;
5318       }
5319     }
5320     m_pDS->close();
5321
5322     if (!scraper || scraper->Content() == CONTENT_NONE)
5323       return ScraperPtr();
5324
5325     if (scraper->Content() == CONTENT_TVSHOWS)
5326     {
5327       settings.recurse = 0;
5328       if(settings.parent_name) // single show
5329       {
5330         settings.parent_name_root = settings.parent_name = (iFound == 1);
5331       }
5332       else // show root
5333       {
5334         settings.parent_name_root = settings.parent_name = (iFound == 2);
5335       }
5336     }
5337     else if (scraper->Content() == CONTENT_MOVIES)
5338     {
5339       settings.recurse = settings.recurse - (iFound-1);
5340       settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
5341     }
5342     else if (scraper->Content() == CONTENT_MUSICVIDEOS)
5343     {
5344       settings.recurse = settings.recurse - (iFound-1);
5345       settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
5346     }
5347     else
5348     {
5349       iFound = 0;
5350       return ScraperPtr();
5351     }
5352     foundDirectly = (iFound == 1);
5353     return scraper;
5354   }
5355   catch (...)
5356   {
5357     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5358   }
5359   return ScraperPtr();
5360 }
5361
5362 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
5363 {
5364   SScanSettings settings;
5365   bool foundDirectly = false;
5366   ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
5367   if (scraper)
5368   {
5369     if (scraper->Content() == CONTENT_TVSHOWS && !foundDirectly)
5370     { // check for episodes or seasons (ASSUMPTION: no episodes == seasons (i.e. assume show/season/episodes structure)
5371       CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
5372       m_pDS->query( sql.c_str() );
5373       if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
5374         return "episodes";
5375       return "seasons";
5376     }
5377     return TranslateContent(scraper->Content());
5378   }
5379   return "";
5380 }
5381
5382 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
5383 {
5384   CStdString strSQL;
5385
5386   try
5387   {
5388     if (NULL == m_pDB.get()) return;
5389     if (NULL == m_pDS.get()) return;
5390
5391     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5392       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());
5393     else
5394       strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
5395     m_pDS->query( strSQL.c_str() );
5396
5397     while (!m_pDS->eof())
5398     {
5399       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5400         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
5401                                                       g_settings.m_videoSources))
5402         {
5403           m_pDS->next();
5404           continue;
5405         }
5406
5407       CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
5408       CStdString strDir;
5409       strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
5410       pItem->m_strPath="videodb://1/1/"+ strDir;
5411       pItem->m_bIsFolder=true;
5412       items.Add(pItem);
5413       m_pDS->next();
5414     }
5415     m_pDS->close();
5416   }
5417   catch (...)
5418   {
5419     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5420   }
5421 }
5422
5423 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
5424 {
5425   CStdString strSQL;
5426
5427   try
5428   {
5429     if (NULL == m_pDB.get()) return;
5430     if (NULL == m_pDS.get()) return;
5431
5432     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5433       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());
5434     else
5435       strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
5436     m_pDS->query( strSQL.c_str() );
5437
5438     while (!m_pDS->eof())
5439     {
5440       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5441         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
5442                                                       g_settings.m_videoSources))
5443         {
5444           m_pDS->next();
5445           continue;
5446         }
5447
5448       CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
5449       CStdString strDir;
5450       strDir.Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
5451       pItem->m_strPath="videodb://1/1/"+ strDir;
5452       pItem->m_bIsFolder=true;
5453       items.Add(pItem);
5454       m_pDS->next();
5455     }
5456     m_pDS->close();
5457   }
5458   catch (...)
5459   {
5460     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5461   }
5462 }
5463
5464 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
5465 {
5466   CStdString strSQL;
5467
5468   try
5469   {
5470     if (NULL == m_pDB.get()) return;
5471     if (NULL == m_pDS.get()) return;
5472
5473     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5474       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());
5475     else
5476       strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
5477     m_pDS->query( strSQL.c_str() );
5478
5479     while (!m_pDS->eof())
5480     {
5481       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5482         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5483         {
5484           m_pDS->next();
5485           continue;
5486         }
5487
5488       CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
5489       CStdString strDir;
5490       strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
5491       pItem->m_strPath="videodb://2/1/"+ strDir;
5492       pItem->m_bIsFolder=true;
5493       items.Add(pItem);
5494       m_pDS->next();
5495     }
5496     m_pDS->close();
5497   }
5498   catch (...)
5499   {
5500     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5501   }
5502 }
5503
5504 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
5505 {
5506   CStdString strSQL;
5507
5508   try
5509   {
5510     if (NULL == m_pDB.get()) return;
5511     if (NULL == m_pDS.get()) return;
5512
5513     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5514       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());
5515     else
5516       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());
5517     m_pDS->query( strSQL.c_str() );
5518
5519     while (!m_pDS->eof())
5520     {
5521       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5522         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5523         {
5524           m_pDS->next();
5525           continue;
5526         }
5527
5528       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
5529       CStdString strDir;
5530       strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
5531       pItem->m_strPath="videodb://1/4/"+ strDir;
5532       pItem->m_bIsFolder=true;
5533       items.Add(pItem);
5534       m_pDS->next();
5535     }
5536     m_pDS->close();
5537   }
5538   catch (...)
5539   {
5540     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5541   }
5542 }
5543
5544 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
5545 {
5546   CStdString strSQL;
5547
5548   try
5549   {
5550     if (NULL == m_pDB.get()) return;
5551     if (NULL == m_pDS.get()) return;
5552
5553     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5554       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());
5555     else
5556       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());
5557     m_pDS->query( strSQL.c_str() );
5558
5559     while (!m_pDS->eof())
5560     {
5561       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5562         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5563         {
5564           m_pDS->next();
5565           continue;
5566         }
5567
5568       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
5569       CStdString strDir;
5570       strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
5571       pItem->m_strPath="videodb://2/4/"+ strDir;
5572       pItem->m_bIsFolder=true;
5573       items.Add(pItem);
5574       m_pDS->next();
5575     }
5576     m_pDS->close();
5577   }
5578   catch (...)
5579   {
5580     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5581   }
5582 }
5583
5584 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
5585 {
5586   CStdString strSQL;
5587
5588   try
5589   {
5590     if (NULL == m_pDB.get()) return;
5591     if (NULL == m_pDS.get()) return;
5592
5593     CStdString strLike;
5594     if (!strSearch.IsEmpty())
5595       strLike = "and actors.strActor like '%%%s%%'";
5596     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5597       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());
5598     else
5599       strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
5600     m_pDS->query( strSQL.c_str() );
5601
5602     while (!m_pDS->eof())
5603     {
5604       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5605         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5606         {
5607           m_pDS->next();
5608           continue;
5609         }
5610
5611       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
5612       CStdString strDir;
5613       strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
5614       pItem->m_strPath="videodb://3/4/"+ strDir;
5615       pItem->m_bIsFolder=true;
5616       items.Add(pItem);
5617       m_pDS->next();
5618     }
5619     m_pDS->close();
5620   }
5621   catch (...)
5622   {
5623     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5624   }
5625 }
5626
5627 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
5628 {
5629   CStdString strSQL;
5630
5631   try
5632   {
5633     if (NULL == m_pDB.get()) return;
5634     if (NULL == m_pDS.get()) return;
5635
5636     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5637       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());
5638     else
5639       strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
5640     m_pDS->query( strSQL.c_str() );
5641
5642     while (!m_pDS->eof())
5643     {
5644       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5645         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5646         {
5647           m_pDS->next();
5648           continue;
5649         }
5650
5651       CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
5652       CStdString strDir;
5653       strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
5654       pItem->m_strPath="videodb://3/1/"+ strDir;
5655       pItem->m_bIsFolder=true;
5656       items.Add(pItem);
5657       m_pDS->next();
5658     }
5659     m_pDS->close();
5660   }
5661   catch (...)
5662   {
5663     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5664   }
5665 }
5666
5667 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
5668 {
5669   CStdString strSQL;
5670
5671   try
5672   {
5673     if (NULL == m_pDB.get()) return;
5674     if (NULL == m_pDS.get()) return;
5675
5676     CStdString strLike;
5677     if (!strSearch.IsEmpty())
5678     {
5679       strLike.Format("and musicvideo.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
5680       strLike += "like '%%s%%%'";
5681     }
5682     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5683       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());
5684     else
5685     {
5686       if (!strLike.IsEmpty())
5687         strLike = "where "+strLike.Mid(4);
5688       strSQL=PrepareSQL("select distinct musicvideo.c%02d,musicvideo.idMVideo from musicvideo"+strLike,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
5689     }
5690     m_pDS->query( strSQL.c_str() );
5691
5692     while (!m_pDS->eof())
5693     {
5694       if (m_pDS->fv(0).get_asString().empty())
5695       {
5696         m_pDS->next();
5697         continue;
5698       }
5699
5700       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5701         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5702         {
5703           m_pDS->next();
5704           continue;
5705         }
5706
5707       CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5708       CStdString strDir;
5709       strDir.Format("%ld", m_pDS->fv(1).get_asInt());
5710       pItem->m_strPath="videodb://3/2/"+ strDir;
5711       pItem->m_bIsFolder=false;
5712       items.Add(pItem);
5713       m_pDS->next();
5714     }
5715     m_pDS->close();
5716   }
5717   catch (...)
5718   {
5719     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5720   }
5721 }
5722
5723 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
5724 {
5725   CStdString strSQL;
5726
5727   try
5728   {
5729     if (NULL == m_pDB.get()) return;
5730     if (NULL == m_pDS.get()) return;
5731
5732     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5733       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());
5734     else
5735       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());
5736     m_pDS->query( strSQL.c_str() );
5737
5738     while (!m_pDS->eof())
5739     {
5740       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5741         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5742         {
5743           m_pDS->next();
5744           continue;
5745         }
5746
5747       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
5748       CStdString strDir;
5749       strDir.Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
5750
5751       pItem->m_strPath="videodb://"+ strDir;
5752       pItem->m_bIsFolder=false;
5753       items.Add(pItem);
5754       m_pDS->next();
5755     }
5756     m_pDS->close();
5757   }
5758   catch (...)
5759   {
5760     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5761   }
5762 }
5763
5764 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const CStdString &whereClause, CFileItemList &items, bool checkLocks /*= true*/)
5765 {
5766   try
5767   {
5768     DWORD time = CTimeUtils::GetTimeMS();
5769     movieTime = 0;
5770     castTime = 0;
5771
5772     if (NULL == m_pDB.get()) return false;
5773     if (NULL == m_pDS.get()) return false;
5774
5775     // We don't use PrepareSQL here, as the WHERE clause is already formatted.
5776     CStdString strSQL = "select * from musicvideoview " + whereClause;
5777     CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
5778
5779     // run query
5780     if (!m_pDS->query(strSQL.c_str()))
5781       return false;
5782     CLog::Log(LOGDEBUG, "%s time for actual SQL query = %d", __FUNCTION__, CTimeUtils::GetTimeMS() - time); time = CTimeUtils::GetTimeMS();
5783
5784     int iRowsFound = m_pDS->num_rows();
5785     if (iRowsFound == 0)
5786     {
5787       m_pDS->close();
5788       return false;
5789     }
5790
5791     // get data from returned rows
5792     items.Reserve(iRowsFound);
5793     // get songs from returned subtable
5794     while (!m_pDS->eof())
5795     {
5796       int idMVideo = m_pDS->fv("idMVideo").get_asInt();
5797       CVideoInfoTag musicvideo = GetDetailsForMusicVideo(m_pDS);
5798       if (!checkLocks || g_settings.GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
5799           g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath,g_settings.m_videoSources))
5800       {
5801         CFileItemPtr item(new CFileItem(musicvideo));
5802         item->m_strPath.Format("%s%ld",baseDir,idMVideo);
5803         item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,musicvideo.m_playCount > 0);
5804         items.Add(item);
5805       }
5806       m_pDS->next();
5807     }
5808
5809     CLog::Log(LOGDEBUG, "%s time to retrieve from dataset = %d", __FUNCTION__, CTimeUtils::GetTimeMS() - time); time = CTimeUtils::GetTimeMS();
5810
5811     // cleanup
5812     m_pDS->close();
5813     return true;
5814   }
5815   catch (...)
5816   {
5817     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, whereClause.c_str());
5818   }
5819   return false;
5820 }
5821
5822 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
5823 {
5824   try
5825   {
5826     if (NULL == m_pDB.get()) return 0;
5827     if (NULL == m_pDS.get()) return 0;
5828
5829     CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
5830     if (!m_pDS->query(strSQL.c_str())) return 0;
5831     songIDs.clear();
5832     if (m_pDS->num_rows() == 0)
5833     {
5834       m_pDS->close();
5835       return 0;
5836     }
5837     songIDs.reserve(m_pDS->num_rows());
5838     while (!m_pDS->eof())
5839     {
5840       songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
5841       m_pDS->next();
5842     }    // cleanup
5843     m_pDS->close();
5844     return songIDs.size();
5845   }
5846   catch (...)
5847   {
5848     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
5849   }
5850   return 0;
5851 }
5852
5853 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
5854 {
5855   try
5856   {
5857     idSong = -1;
5858
5859     int iCount = GetMusicVideoCount(strWhere);
5860     if (iCount <= 0)
5861       return false;
5862     int iRandom = rand() % iCount;
5863
5864     if (NULL == m_pDB.get()) return false;
5865     if (NULL == m_pDS.get()) return false;
5866
5867     // We don't use PrepareSQL here, as the WHERE clause is already formatted.
5868     CStdString strSQL;
5869     strSQL.Format("select * from musicvideoview %s order by idMVideo limit 1 offset %i",strWhere.c_str(),iRandom);
5870     CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
5871     // run query
5872     if (!m_pDS->query(strSQL.c_str()))
5873       return false;
5874     int iRowsFound = m_pDS->num_rows();
5875     if (iRowsFound != 1)
5876     {
5877       m_pDS->close();
5878       return false;
5879     }
5880     *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
5881     item->m_strPath.Format("videodb://3/2/%ld",item->GetVideoInfoTag()->m_iDbId);
5882     idSong = m_pDS->fv("idMVideo").get_asInt();
5883     item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
5884     m_pDS->close();
5885     return true;
5886   }
5887   catch(...)
5888   {
5889     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
5890   }
5891   return false;
5892 }
5893
5894 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
5895 {
5896   try
5897   {
5898     if (NULL == m_pDB.get()) return -1;
5899     if (NULL == m_pDS.get()) return -1;
5900
5901     CStdString strSQL;
5902     if (strAlbum.IsEmpty() && strTitle.IsEmpty())
5903     { // we want to return matching artists only
5904       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5905         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());
5906       else
5907         strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
5908     }
5909     else
5910     { // we want to return the matching musicvideo
5911       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5912         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());
5913       else
5914         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());
5915     }
5916     m_pDS->query( strSQL.c_str() );
5917
5918     if (m_pDS->eof())
5919       return -1;
5920
5921     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5922       if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5923       {
5924         m_pDS->close();
5925         return -1;
5926       }
5927
5928     int lResult = m_pDS->fv(0).get_asInt();
5929     m_pDS->close();
5930     return lResult;
5931   }
5932   catch (...)
5933   {
5934     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5935   }
5936   return -1;
5937 }
5938
5939 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
5940 {
5941   CStdString strSQL;
5942
5943   try
5944   {
5945     if (NULL == m_pDB.get()) return;
5946     if (NULL == m_pDS.get()) return;
5947
5948     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5949       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%%'",VIDEODB_ID_TITLE,VIDEODB_ID_TITLE,strSearch.c_str());
5950     else
5951       strSQL = PrepareSQL("select movie.idMovie,movie.c%02d from movie where movie.c%02d like '%%%s%%'",VIDEODB_ID_TITLE,VIDEODB_ID_TITLE,strSearch.c_str());
5952     m_pDS->query( strSQL.c_str() );
5953
5954     while (!m_pDS->eof())
5955     {
5956       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5957         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
5958         {
5959           m_pDS->next();
5960           continue;
5961         }
5962
5963       int movieId = m_pDS->fv("movie.idMovie").get_asInt();
5964       CStdString strSQL2 = PrepareSQL("select idSet from setlinkmovie where idMovie=%i",movieId); 
5965       m_pDS2->query(strSQL2.c_str());
5966       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5967       if (m_pDS2->eof())
5968         pItem->m_strPath.Format("videodb://1/2/%i",movieId);
5969       else
5970         pItem->m_strPath.Format("videodb://1/7/%i/%i",m_pDS2->fv(0).get_asInt(),movieId);
5971
5972       pItem->m_bIsFolder=false;
5973       items.Add(pItem);
5974       m_pDS2->close();
5975       m_pDS->next();
5976     }
5977     m_pDS->close();
5978   }
5979   catch (...)
5980   {
5981     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
5982   }
5983 }
5984
5985 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
5986 {
5987   CStdString strSQL;
5988
5989   try
5990   {
5991     if (NULL == m_pDB.get()) return;
5992     if (NULL == m_pDS.get()) return;
5993
5994     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5995       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());
5996     else
5997       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());
5998     m_pDS->query( strSQL.c_str() );
5999
6000     while (!m_pDS->eof())
6001     {
6002       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6003         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6004         {
6005           m_pDS->next();
6006           continue;
6007         }
6008
6009       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
6010       CStdString strDir;
6011       strDir.Format("2/2/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
6012
6013       pItem->m_strPath="videodb://"+ strDir;
6014       pItem->m_bIsFolder=true;
6015       pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
6016       items.Add(pItem);
6017       m_pDS->next();
6018     }
6019     m_pDS->close();
6020   }
6021   catch (...)
6022   {
6023     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6024   }
6025 }
6026
6027 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
6028 {
6029   CStdString strSQL;
6030
6031   try
6032   {
6033     if (NULL == m_pDB.get()) return;
6034     if (NULL == m_pDS.get()) return;
6035
6036     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6037       strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,tvshowlinkepisode.idShow,tvshow.c%02d,path.strPath from episode,files,path,tvshowlinkepisode,tvshow where files.idFile=episode.idFile and tvshowlinkepisode.idEpisode=episode.idEpisode and tvshowlinkepisode.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());
6038     else
6039       strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,tvshowlinkepisode.idShow,tvshow.c%02d from episode,tvshowlinkepisode,tvshow where tvshowlinkepisode.idEpisode=episode.idEpisode and tvshow.idShow=tvshowlinkepisode.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());
6040     m_pDS->query( strSQL.c_str() );
6041
6042     while (!m_pDS->eof())
6043     {
6044       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6045         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6046         {
6047           m_pDS->next();
6048           continue;
6049         }
6050
6051       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
6052       pItem->m_strPath.Format("videodb://2/2/%ld/%ld/%ld",m_pDS->fv("tvshowlinkepisode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
6053       pItem->m_bIsFolder=false;
6054       items.Add(pItem);
6055       m_pDS->next();
6056     }
6057     m_pDS->close();
6058   }
6059   catch (...)
6060   {
6061     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6062   }
6063 }
6064
6065 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
6066 {
6067 // Alternative searching - not quite as fast though due to
6068 // retrieving all information
6069 //  CStdString where = PrepareSQL("where c%02d like '%s%%' or c%02d like '%% %s%%'", VIDEODB_ID_MUSICVIDEO_TITLE, strSearch.c_str(), VIDEODB_ID_MUSICVIDEO_TITLE, strSearch.c_str());
6070 //  GetMusicVideosByWhere("videodb://3/2/", where, items);
6071   CStdString strSQL;
6072
6073   try
6074   {
6075     if (NULL == m_pDB.get()) return;
6076     if (NULL == m_pDS.get()) return;
6077
6078     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6079       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());
6080     else
6081       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());
6082     m_pDS->query( strSQL.c_str() );
6083
6084     while (!m_pDS->eof())
6085     {
6086       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6087         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6088         {
6089           m_pDS->next();
6090           continue;
6091         }
6092
6093       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
6094       CStdString strDir;
6095       strDir.Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
6096
6097       pItem->m_strPath="videodb://"+ strDir;
6098       pItem->m_bIsFolder=false;
6099       items.Add(pItem);
6100       m_pDS->next();
6101     }
6102     m_pDS->close();
6103   }
6104   catch (...)
6105   {
6106     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6107   }
6108 }
6109
6110 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
6111 {
6112 // Alternative searching - not quite as fast though due to
6113 // retrieving all information
6114 //  CStdString where = PrepareSQL("where c%02d like '%s%%' or c%02d like '%% %s%%'", VIDEODB_ID_EPISODE_PLOT, strSearch.c_str(), VIDEODB_ID_EPISODE_PLOT, strSearch.c_str());
6115 //  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());
6116 //  GetEpisodesByWhere("videodb://2/2/", where, items);
6117 //  return;
6118   CStdString strSQL;
6119
6120   try
6121   {
6122     if (NULL == m_pDB.get()) return;
6123     if (NULL == m_pDS.get()) return;
6124
6125     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6126       strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,tvshowlinkepisode.idShow,tvshow.c%02d,path.strPath from episode,files,path,tvshowlinkepisode,tvshow where files.idFile=episode.idFile and tvshowlinkepisode.idEpisode=episode.idEpisode and files.idPath=path.idPath and tvshow.idShow=tvshowlinkepisode.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());
6127     else
6128       strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,tvshowlinkepisode.idShow,tvshow.c%02d from episode,tvshowlinkepisode,tvshow where tvshowlinkepisode.idEpisode=episode.idEpisode and tvshow.idShow=tvshowlinkepisode.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());
6129     m_pDS->query( strSQL.c_str() );
6130
6131     while (!m_pDS->eof())
6132     {
6133       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6134         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6135         {
6136           m_pDS->next();
6137           continue;
6138         }
6139
6140       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
6141       pItem->m_strPath.Format("videodb://2/2/%ld/%ld/%ld",m_pDS->fv("tvshowlinkepisode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
6142       pItem->m_bIsFolder=false;
6143       items.Add(pItem);
6144       m_pDS->next();
6145     }
6146     m_pDS->close();
6147   }
6148   catch (...)
6149   {
6150     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6151   }
6152 }
6153
6154 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
6155 {
6156   CStdString strSQL;
6157
6158   try
6159   {
6160     if (NULL == m_pDB.get()) return;
6161     if (NULL == m_pDS.get()) return;
6162
6163     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6164       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());
6165     else
6166       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());
6167
6168     m_pDS->query( strSQL.c_str() );
6169
6170     while (!m_pDS->eof())
6171     {
6172       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6173         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),g_settings.m_videoSources))
6174         {
6175           m_pDS->next();
6176           continue;
6177         }
6178
6179       CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
6180       pItem->m_strPath.Format("videodb://1/2/%ld", m_pDS->fv(0).get_asInt());
6181       pItem->m_bIsFolder=false;
6182
6183       items.Add(pItem);
6184       m_pDS->next();
6185     }
6186     m_pDS->close();
6187
6188   }
6189   catch (...)
6190   {
6191     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6192   }
6193 }
6194
6195 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
6196 {
6197   CStdString strSQL;
6198
6199   try
6200   {
6201     if (NULL == m_pDB.get()) return;
6202     if (NULL == m_pDS.get()) return;
6203
6204     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6205       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());
6206     else
6207       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());
6208
6209     m_pDS->query( strSQL.c_str() );
6210
6211     while (!m_pDS->eof())
6212     {
6213       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6214         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6215         {
6216           m_pDS->next();
6217           continue;
6218         }
6219
6220       CStdString strDir;
6221       strDir.Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
6222       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6223
6224       pItem->m_strPath="videodb://1/5/"+ strDir;
6225       pItem->m_bIsFolder=true;
6226       items.Add(pItem);
6227       m_pDS->next();
6228     }
6229     m_pDS->close();
6230   }
6231   catch (...)
6232   {
6233     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6234   }
6235 }
6236
6237 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
6238 {
6239   CStdString strSQL;
6240
6241   try
6242   {
6243     if (NULL == m_pDB.get()) return;
6244     if (NULL == m_pDS.get()) return;
6245
6246     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6247       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());
6248     else
6249       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());
6250
6251     m_pDS->query( strSQL.c_str() );
6252
6253     while (!m_pDS->eof())
6254     {
6255       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6256         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6257         {
6258           m_pDS->next();
6259           continue;
6260         }
6261
6262       CStdString strDir;
6263       strDir.Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
6264       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6265
6266       pItem->m_strPath="videodb://2/5/"+ strDir;
6267       pItem->m_bIsFolder=true;
6268       items.Add(pItem);
6269       m_pDS->next();
6270     }
6271     m_pDS->close();
6272   }
6273   catch (...)
6274   {
6275     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6276   }
6277 }
6278
6279 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
6280 {
6281   CStdString strSQL;
6282
6283   try
6284   {
6285     if (NULL == m_pDB.get()) return;
6286     if (NULL == m_pDS.get()) return;
6287
6288     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6289       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());
6290     else
6291       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());
6292
6293     m_pDS->query( strSQL.c_str() );
6294
6295     while (!m_pDS->eof())
6296     {
6297       if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6298         if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),g_settings.m_videoSources))
6299         {
6300           m_pDS->next();
6301           continue;
6302         }
6303
6304       CStdString strDir;
6305       strDir.Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
6306       CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
6307
6308       pItem->m_strPath="videodb://3/5/"+ strDir;
6309       pItem->m_bIsFolder=true;
6310       items.Add(pItem);
6311       m_pDS->next();
6312     }
6313     m_pDS->close();
6314   }
6315   catch (...)
6316   {
6317     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6318   }
6319 }
6320
6321 void CVideoDatabase::CleanDatabase(IVideoInfoScannerObserver* pObserver, const vector<int>* paths)
6322 {
6323   CGUIDialogProgress *progress=NULL;
6324   try
6325   {
6326     if (NULL == m_pDB.get()) return;
6327     if (NULL == m_pDS.get()) return;
6328
6329     unsigned int time = CTimeUtils::GetTimeMS();
6330     CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
6331
6332     BeginTransaction();
6333
6334     // find all the files
6335     CStdString sql;
6336     if (paths)
6337     {
6338       if (paths->size() == 0)
6339       {
6340         RollbackTransaction();
6341         return;
6342       }
6343
6344       CStdString strPaths;
6345       for (unsigned int i=0;i<paths->size();++i )
6346         strPaths.Format("%s,%i",strPaths.Mid(0).c_str(),paths->at(i));
6347       sql = PrepareSQL("select * from files,path where files.idPath=path.idPath and path.idPath in (%s)",strPaths.Mid(1).c_str());
6348     }
6349     else
6350       sql = "select * from files, path where files.idPath = path.idPath";
6351
6352     m_pDS->query(sql.c_str());
6353     if (m_pDS->num_rows() == 0) return;
6354
6355     if (!pObserver)
6356     {
6357       progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
6358       if (progress)
6359       {
6360         progress->SetHeading(700);
6361         progress->SetLine(0, "");
6362         progress->SetLine(1, 313);
6363         progress->SetLine(2, 330);
6364         progress->SetPercentage(0);
6365         progress->StartModal();
6366         progress->ShowProgressBar(true);
6367       }
6368     }
6369     else
6370     {
6371       pObserver->OnDirectoryChanged("");
6372       pObserver->OnSetTitle("");
6373       pObserver->OnSetCurrentProgress(0,1);
6374       pObserver->OnStateChanged(CLEANING_UP_DATABASE);
6375     }
6376
6377     CStdString filesToDelete = "";
6378     CStdString moviesToDelete = "";
6379     CStdString episodesToDelete = "";
6380     CStdString musicVideosToDelete = "";
6381
6382     std::vector<int> movieIDs;
6383     std::vector<int> episodeIDs;
6384     std::vector<int> musicVideoIDs;
6385
6386     int total = m_pDS->num_rows();
6387     int current = 0;
6388     while (!m_pDS->eof())
6389     {
6390       CStdString path = m_pDS->fv("path.strPath").get_asString();
6391       CStdString fileName = m_pDS->fv("files.strFileName").get_asString();
6392       CStdString fullPath;
6393       ConstructPath(fullPath,path,fileName);
6394
6395       // get the first stacked file
6396       if (URIUtils::IsStack(fullPath))
6397         fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
6398
6399       // check for deletion
6400       bool bIsSource;
6401       VECSOURCES *pShares = g_settings.GetSourcesFromType("video");
6402
6403       // check if we have a internet related file that is part of a media source
6404       if (URIUtils::IsInternetStream(fullPath, true) && CUtil::GetMatchingSource(fullPath, *pShares, bIsSource) > -1)
6405       {
6406         if (!CFile::Exists(fullPath, false))
6407           filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
6408       }
6409       else
6410       {
6411         // remove optical, internet related and non-existing files
6412         // note: this will also remove entries from previously existing media sources
6413         if (URIUtils::IsOnDVD(fullPath) || URIUtils::IsInternetStream(fullPath, true) || !CFile::Exists(fullPath, false))
6414           filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
6415       }
6416
6417       if (!pObserver)
6418       {
6419         if (progress)
6420         {
6421           progress->SetPercentage(current * 100 / total);
6422           progress->Progress();
6423           if (progress->IsCanceled())
6424           {
6425             progress->Close();
6426             m_pDS->close();
6427             return;
6428           }
6429         }
6430       }
6431       else
6432         pObserver->OnSetProgress(current,total);
6433
6434       m_pDS->next();
6435       current++;
6436     }
6437     m_pDS->close();
6438
6439     if ( ! filesToDelete.IsEmpty() )
6440     {
6441       filesToDelete.TrimRight(",");
6442       // now grab them movies
6443       sql = PrepareSQL("select idMovie from movie where idFile in (%s)",filesToDelete.c_str());
6444       m_pDS->query(sql.c_str());
6445       while (!m_pDS->eof())
6446       {
6447         movieIDs.push_back(m_pDS->fv(0).get_asInt());
6448         moviesToDelete += m_pDS->fv(0).get_asString() + ",";
6449         m_pDS->next();
6450       }
6451       m_pDS->close();
6452       // now grab them episodes
6453       sql = PrepareSQL("select idEpisode from episode where idFile in (%s)",filesToDelete.c_str());
6454        m_pDS->query(sql.c_str());
6455       while (!m_pDS->eof())
6456       {
6457         episodeIDs.push_back(m_pDS->fv(0).get_asInt());
6458         episodesToDelete += m_pDS->fv(0).get_asString() + ",";
6459         m_pDS->next();
6460       }
6461       m_pDS->close();
6462
6463       // now grab them musicvideos
6464       sql = PrepareSQL("select idMVideo from musicvideo where idFile in (%s)",filesToDelete.c_str());
6465       m_pDS->query(sql.c_str());
6466       while (!m_pDS->eof())
6467       {
6468         musicVideoIDs.push_back(m_pDS->fv(0).get_asInt());
6469         musicVideosToDelete += m_pDS->fv(0).get_asString() + ",";
6470         m_pDS->next();
6471       }
6472       m_pDS->close();
6473     }
6474
6475     if (progress)
6476     {
6477       progress->SetPercentage(100);
6478       progress->Progress();
6479     }
6480
6481     if ( ! filesToDelete.IsEmpty() )
6482     {
6483       filesToDelete = "(" + filesToDelete + ")";
6484       CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
6485       sql = "delete from files where idFile in " + filesToDelete;
6486       m_pDS->exec(sql.c_str());
6487
6488       CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
6489       sql = "delete from streamdetails where idFile in " + filesToDelete;
6490       m_pDS->exec(sql.c_str());
6491
6492       CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
6493       sql = "delete from bookmark where idFile in " + filesToDelete;
6494       m_pDS->exec(sql.c_str());
6495
6496       CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
6497       sql = "delete from settings where idFile in " + filesToDelete;
6498       m_pDS->exec(sql.c_str());
6499
6500       CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
6501       sql = "delete from stacktimes where idFile in " + filesToDelete;
6502       m_pDS->exec(sql.c_str());
6503     }
6504
6505     if ( ! moviesToDelete.IsEmpty() )
6506     {
6507       moviesToDelete = "(" + moviesToDelete.TrimRight(",") + ")";
6508
6509       CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
6510       sql = "delete from movie where idMovie in " + moviesToDelete;
6511       m_pDS->exec(sql.c_str());
6512
6513       CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
6514       sql = "delete from actorlinkmovie where idMovie in " + moviesToDelete;
6515       m_pDS->exec(sql.c_str());
6516
6517       CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
6518       sql = "delete from directorlinkmovie where idMovie in " + moviesToDelete;
6519       m_pDS->exec(sql.c_str());
6520
6521       CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
6522       sql = "delete from writerlinkmovie where idMovie in " + moviesToDelete;
6523       m_pDS->exec(sql.c_str());
6524
6525       CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
6526       sql = "delete from genrelinkmovie where idMovie in " + moviesToDelete;
6527       m_pDS->exec(sql.c_str());
6528
6529       CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
6530       sql = "delete from countrylinkmovie where idMovie in " + moviesToDelete;
6531       m_pDS->exec(sql.c_str());
6532
6533       CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
6534       sql = "delete from studiolinkmovie where idMovie in " + moviesToDelete;
6535       m_pDS->exec(sql.c_str());
6536
6537       CLog::Log(LOGDEBUG, "%s: Cleaning setlinkmovie table", __FUNCTION__);
6538       sql = "delete from setlinkmovie where idMovie in " + moviesToDelete;
6539       m_pDS->exec(sql.c_str());
6540     }
6541
6542     if ( ! episodesToDelete.IsEmpty() )
6543     {
6544       episodesToDelete = "(" + episodesToDelete.TrimRight(",") + ")";
6545
6546       CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
6547       sql = "delete from episode where idEpisode in " + episodesToDelete;
6548       m_pDS->exec(sql.c_str());
6549
6550       CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
6551       sql = "delete from actorlinkepisode where idEpisode in " + episodesToDelete;
6552       m_pDS->exec(sql.c_str());
6553
6554       CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
6555       sql = "delete from directorlinkepisode where idEpisode in " + episodesToDelete;
6556       m_pDS->exec(sql.c_str());
6557
6558       CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
6559       sql = "delete from writerlinkepisode where idEpisode in " + episodesToDelete;
6560       m_pDS->exec(sql.c_str());
6561
6562       CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkepisode table", __FUNCTION__);
6563       sql = "delete from tvshowlinkepisode where idEpisode in " + episodesToDelete;
6564       m_pDS->exec(sql.c_str());
6565     }
6566
6567     CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and don't have content set...", __FUNCTION__);
6568     sql = "select * from path where strContent not like ''";
6569     m_pDS->query(sql.c_str());
6570     CStdString strIds;
6571     while (!m_pDS->eof())
6572     {
6573       if (!CDirectory::Exists(m_pDS->fv("path.strPath").get_asString()))
6574         strIds.Format("%s %i,",strIds.Mid(0),m_pDS->fv("path.idPath").get_asInt()); // mid since we cannot format the same string
6575       m_pDS->next();
6576     }
6577     if (!strIds.IsEmpty())
6578     {
6579       strIds.TrimLeft(" ");
6580       strIds.TrimRight(",");
6581       sql = PrepareSQL("delete from path where idPath in (%s)",strIds.c_str());
6582       m_pDS->exec(sql.c_str());
6583       sql = PrepareSQL("delete from tvshowlinkpath where idPath in (%s)",strIds.c_str());
6584       m_pDS->exec(sql.c_str());
6585     }
6586
6587     CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
6588     sql = "delete from tvshow where idShow not in (select idShow from tvshowlinkpath)";
6589     m_pDS->exec(sql.c_str());
6590
6591     std::vector<int> tvshowIDs;
6592     CStdString showsToDelete;
6593     sql = "select tvshow.idShow from tvshow "
6594             "join tvshowlinkpath on tvshow.idShow=tvshowlinkpath.idShow "
6595             "join path on path.idPath=tvshowlinkpath.idPath "
6596           "where tvshow.idShow not in (select idShow from tvshowlinkepisode) "
6597             "and path.strContent=''";
6598     m_pDS->query(sql.c_str());
6599     while (!m_pDS->eof())
6600     {
6601       tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
6602       showsToDelete += m_pDS->fv(0).get_asString() + ",";
6603       m_pDS->next();
6604     }
6605     m_pDS->close();
6606     if (!showsToDelete.IsEmpty())
6607     {
6608       sql = "delete from tvshow where idShow in (" + showsToDelete.TrimRight(",") + ")";
6609       m_pDS->exec(sql.c_str());
6610     }
6611
6612     CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
6613     sql = "delete from actorlinktvshow where idShow not in (select idShow from tvshow)";
6614     m_pDS->exec(sql.c_str());
6615
6616     CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
6617     sql = "delete from directorlinktvshow where idShow not in (select idShow from tvshow)";
6618     m_pDS->exec(sql.c_str());
6619
6620     CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
6621     sql = "delete from tvshowlinkpath where idShow not in (select idShow from tvshow)";
6622     m_pDS->exec(sql.c_str());
6623
6624     CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
6625     sql = "delete from genrelinktvshow where idShow not in (select idShow from tvshow)";
6626     m_pDS->exec(sql.c_str());
6627
6628     CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
6629     sql = "delete from movielinktvshow where idShow not in (select idShow from tvshow)";
6630     m_pDS->exec(sql.c_str());
6631     sql = "delete from movielinktvshow where idMovie not in (select distinct idMovie from movie)";
6632     m_pDS->exec(sql.c_str());
6633
6634     if ( ! musicVideosToDelete.IsEmpty() )
6635     {
6636       musicVideosToDelete = "(" + musicVideosToDelete.TrimRight(",") + ")";
6637
6638       CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
6639       sql = "delete from musicvideo where idMVideo in " + musicVideosToDelete;
6640       m_pDS->exec(sql.c_str());
6641
6642       CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
6643       sql = "delete from artistlinkmusicvideo where idMVideo in " + musicVideosToDelete;
6644       m_pDS->exec(sql.c_str());
6645
6646       CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
6647       sql = "delete from directorlinkmusicvideo where idMVideo in " + musicVideosToDelete;
6648       m_pDS->exec(sql.c_str());
6649
6650       CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
6651       sql = "delete from genrelinkmusicvideo where idMVideo in " + musicVideosToDelete;
6652       m_pDS->exec(sql.c_str());
6653
6654       CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
6655       sql = "delete from studiolinkmusicvideo where idMVideo in " + musicVideosToDelete;
6656       m_pDS->exec(sql.c_str());
6657     }
6658
6659     CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
6660     sql = "delete from path where idPath not in (select distinct idPath from files) and idPath not in (select distinct idPath from tvshowlinkpath) and strContent=''";
6661     m_pDS->exec(sql.c_str());
6662
6663     CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
6664     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)";
6665     m_pDS->exec(sql.c_str());
6666
6667     CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
6668     sql = "delete from country where idCountry not in (select distinct idCountry from countrylinkmovie)";
6669     m_pDS->exec(sql.c_str());
6670
6671     CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
6672     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)";
6673     m_pDS->exec(sql.c_str());
6674
6675     CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
6676     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)";
6677     m_pDS->exec(sql.c_str());
6678
6679     CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
6680     sql = "delete from sets where idSet not in (select distinct idSet from setlinkmovie)";
6681     m_pDS->exec(sql.c_str());
6682
6683     CommitTransaction();
6684
6685     if (pObserver)
6686       pObserver->OnStateChanged(COMPRESSING_DATABASE);
6687
6688     Compress(false);
6689
6690     CUtil::DeleteVideoDatabaseDirectoryCache();
6691
6692     time = CTimeUtils::GetTimeMS() - time;
6693     CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
6694
6695     for (unsigned int i = 0; i < movieIDs.size(); i++)
6696       AnnounceRemove("movie", movieIDs[i]);
6697
6698     for (unsigned int i = 0; i < episodeIDs.size(); i++)
6699       AnnounceRemove("episode", episodeIDs[i]);
6700
6701     for (unsigned int i = 0; i < tvshowIDs.size(); i++)
6702       AnnounceRemove("tvshow", tvshowIDs[i]);
6703
6704     for (unsigned int i = 0; i < musicVideoIDs.size(); i++)
6705       AnnounceRemove("musicvideo", musicVideoIDs[i]);
6706   }
6707   catch (...)
6708   {
6709     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6710   }
6711   if (progress)
6712     progress->Close();
6713 }
6714
6715 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
6716 {
6717   // get all tvshows
6718   CFileItemList items;
6719   GetTvShowsByWhere("videodb://2/2/", "", items);
6720   CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
6721   CDirectory::Create(showPath);
6722   for (int i = 0; i < items.Size(); i++)
6723   {
6724     // create a folder in this directory
6725     CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
6726     CStdString TVFolder;
6727     URIUtils::AddFileToFolder(showPath, showName, TVFolder);
6728     if (CDirectory::Create(TVFolder))
6729     { // right - grab the episodes and dump them as well
6730       CFileItemList episodes;
6731       CStdString where = PrepareSQL("where idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId);
6732       GetEpisodesByWhere("videodb://2/2/", where, episodes);
6733       for (int i = 0; i < episodes.Size(); i++)
6734       {
6735         CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
6736         CStdString episode;
6737         episode.Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
6738         // and make a file
6739         CStdString episodePath;
6740         URIUtils::AddFileToFolder(TVFolder, episode, episodePath);
6741         CFile file;
6742         if (file.OpenForWrite(episodePath))
6743           file.Close();
6744       }
6745     }
6746   }
6747   // get all movies
6748   items.Clear();
6749   GetMoviesByWhere("videodb://1/2/", "", "", items);
6750   CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
6751   CDirectory::Create(moviePath);
6752   for (int i = 0; i < items.Size(); i++)
6753   {
6754     CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
6755     CStdString movie;
6756     movie.Format("%s.avi", tag->m_strTitle.c_str());
6757     CFile file;
6758     if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
6759       file.Close();
6760   }
6761 }
6762
6763 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
6764 {
6765   CGUIDialogProgress *progress=NULL;
6766   try
6767   {
6768     if (NULL == m_pDB.get()) return;
6769     if (NULL == m_pDS.get()) return;
6770     if (NULL == m_pDS2.get()) return;
6771
6772     // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
6773     auto_ptr<Dataset> pDS;
6774     pDS.reset(m_pDB->CreateDataset());
6775     if (NULL == pDS.get()) return;
6776
6777     auto_ptr<Dataset> pDS2;
6778     pDS2.reset(m_pDB->CreateDataset());
6779     if (NULL == pDS2.get()) return;
6780
6781     // if we're exporting to a single folder, we export thumbs as well
6782     CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
6783     CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
6784     CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
6785     CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
6786     CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
6787     CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
6788     if (!singleFiles)
6789     {
6790       images = true;
6791       overwrite = false;
6792       actorThumbs = true;
6793       CDirectory::Remove(exportRoot);
6794       CDirectory::Create(exportRoot);
6795       CDirectory::Create(actorsDir);
6796       CDirectory::Create(moviesDir);
6797       CDirectory::Create(musicvideosDir);
6798       CDirectory::Create(tvshowsDir);
6799     }
6800
6801     progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
6802     // find all movies
6803     CStdString sql = "select * from movieview";
6804
6805     m_pDS->query(sql.c_str());
6806
6807     if (progress)
6808     {
6809       progress->SetHeading(647);
6810       progress->SetLine(0, 650);
6811       progress->SetLine(1, "");
6812       progress->SetLine(2, "");
6813       progress->SetPercentage(0);
6814       progress->StartModal();
6815       progress->ShowProgressBar(true);
6816     }
6817
6818     int total = m_pDS->num_rows();
6819     int current = 0;
6820
6821     // create our xml document
6822     TiXmlDocument xmlDoc;
6823     TiXmlDeclaration decl("1.0", "UTF-8", "yes");
6824     xmlDoc.InsertEndChild(decl);
6825     TiXmlNode *pMain = NULL;
6826     if (singleFiles)
6827       pMain = &xmlDoc;
6828     else
6829     {
6830       TiXmlElement xmlMainElement("videodb");
6831       pMain = xmlDoc.InsertEndChild(xmlMainElement);
6832       XMLUtils::SetInt(pMain,"version", GetExportVersion());
6833     }
6834
6835     while (!m_pDS->eof())
6836     {
6837       CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
6838       // strip paths to make them relative
6839       if (movie.m_strTrailer.Mid(0,movie.m_strPath.size()).Equals(movie.m_strPath))
6840         movie.m_strTrailer = movie.m_strTrailer.Mid(movie.m_strPath.size());
6841       movie.Save(pMain, "movie", !singleFiles);
6842
6843       // reset old skip state
6844       bool bSkip = false;
6845
6846       if (progress)
6847       {
6848         progress->SetLine(1, movie.m_strTitle);
6849         progress->SetPercentage(current * 100 / total);
6850         progress->Progress();
6851         if (progress->IsCanceled())
6852         {
6853           progress->Close();
6854           m_pDS->close();
6855           return;
6856         }
6857       }
6858
6859       CFileItem item(movie.m_strFileNameAndPath,false);
6860       CFileItem saveItem(item);
6861       if (!singleFiles)
6862       {
6863         CStdString strFileName(movie.m_strTitle);
6864         if (movie.m_iYear > 0)
6865           strFileName.AppendFormat("_%i", movie.m_iYear);
6866         saveItem = CFileItem(GetSafeFile(moviesDir, strFileName) + ".avi", false);
6867       }
6868       if (singleFiles && CUtil::SupportsFileOperations(movie.m_strFileNameAndPath))
6869       {
6870         if (!item.Exists(false))
6871         {
6872           CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
6873           bSkip = true;
6874         }
6875         else
6876         {
6877           CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
6878
6879           if (item.IsOpticalMediaFile())
6880           {
6881             nfoFile = URIUtils::GetParentFolderURI(nfoFile, true);
6882           }
6883
6884           if (overwrite || !CFile::Exists(nfoFile, false))
6885           {
6886             if(!xmlDoc.SaveFile(nfoFile))
6887             {
6888               CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
6889               bSkip = ExportSkipEntry(nfoFile);
6890               if (!bSkip)
6891               {
6892                 if (progress)
6893                 {
6894                   progress->Close();
6895                   m_pDS->close();
6896                   return;
6897                 }
6898               }
6899             }
6900           }
6901
6902           xmlDoc.Clear();
6903           TiXmlDeclaration decl("1.0", "UTF-8", "yes");
6904           xmlDoc.InsertEndChild(decl);
6905         }
6906       }
6907
6908       if (images && !bSkip)
6909       {
6910         CStdString cachedThumb(GetCachedThumb(item));
6911         CStdString savedThumb(saveItem.GetTBNFile());
6912         if (!cachedThumb.IsEmpty() && (overwrite || !CFile::Exists(savedThumb, false)))
6913           if (!CFile::Cache(cachedThumb, savedThumb))
6914             CLog::Log(LOGERROR, "%s: Movie thumb export failed! ('%s' -> '%s')", __FUNCTION__, cachedThumb.c_str(), savedThumb.c_str());
6915         
6916         CStdString cachedFanart(item.GetCachedFanart());
6917         CStdString savedFanart(URIUtils::ReplaceExtension(savedThumb, "-fanart.jpg"));
6918         
6919         if (CFile::Exists(cachedFanart, false) && (overwrite || !CFile::Exists(savedFanart, false)))
6920           if (!CFile::Cache(cachedFanart, savedFanart))
6921             CLog::Log(LOGERROR, "%s: Movie fanart export failed! ('%s' -> '%s')", __FUNCTION__, cachedFanart.c_str(), savedFanart.c_str());
6922         
6923         if (actorThumbs)
6924           ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
6925       }
6926       m_pDS->next();
6927       current++;
6928     }
6929     m_pDS->close();
6930
6931     // find all musicvideos
6932     sql = "select * from musicvideoview";
6933
6934     m_pDS->query(sql.c_str());
6935
6936     total = m_pDS->num_rows();
6937     current = 0;
6938
6939     while (!m_pDS->eof())
6940     {
6941       CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS);
6942       movie.Save(pMain, "musicvideo", !singleFiles);
6943
6944       // reset old skip state
6945       bool bSkip = false;
6946
6947       if (progress)
6948       {
6949         progress->SetLine(1, movie.m_strTitle);
6950         progress->SetPercentage(current * 100 / total);
6951         progress->Progress();
6952         if (progress->IsCanceled())
6953         {
6954           progress->Close();
6955           m_pDS->close();
6956           return;
6957         }
6958       }
6959
6960       CFileItem item(movie.m_strFileNameAndPath,false);
6961       CFileItem saveItem(item);
6962       if (!singleFiles)
6963       {
6964         CStdString strFileName(movie.m_strArtist + "." + movie.m_strTitle);
6965         if (movie.m_iYear > 0)
6966           strFileName.AppendFormat("_%i", movie.m_iYear);
6967         saveItem = CFileItem(GetSafeFile(musicvideosDir, strFileName) + ".avi", false);
6968       }
6969       if (CUtil::SupportsFileOperations(movie.m_strFileNameAndPath) && singleFiles)
6970       {
6971         if (!item.Exists(false))
6972         {
6973           CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
6974           bSkip = true;
6975         }
6976         else
6977         {
6978           CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
6979
6980           if (overwrite || !CFile::Exists(nfoFile, false))
6981           {
6982             if(!xmlDoc.SaveFile(nfoFile))
6983             {
6984               CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
6985               bSkip = ExportSkipEntry(nfoFile);
6986               if (!bSkip)
6987               {
6988                 if (progress)
6989                 {
6990                   progress->Close();
6991                   m_pDS->close();
6992                   return;
6993                 }
6994               }
6995             }
6996           }
6997
6998           xmlDoc.Clear();
6999           TiXmlDeclaration decl("1.0", "UTF-8", "yes");
7000           xmlDoc.InsertEndChild(decl);
7001         }
7002       }
7003       if (images && !bSkip)
7004       {
7005         CStdString cachedThumb(GetCachedThumb(item));
7006         CStdString savedThumb(saveItem.GetTBNFile());
7007         if (!cachedThumb.IsEmpty() && (overwrite || !CFile::Exists(savedThumb, false)))
7008           if (!CFile::Cache(cachedThumb, savedThumb))
7009             CLog::Log(LOGERROR, "%s: Musicvideo thumb export failed! ('%s' -> '%s')", __FUNCTION__, cachedThumb.c_str(), savedThumb.c_str());
7010       }
7011       m_pDS->next();
7012       current++;
7013     }
7014     m_pDS->close();
7015
7016     // repeat for all tvshows
7017     sql = "SELECT * FROM tvshowview";
7018     m_pDS->query(sql.c_str());
7019
7020     total = m_pDS->num_rows();
7021     current = 0;
7022
7023     while (!m_pDS->eof())
7024     {
7025       CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
7026       tvshow.Save(pMain, "tvshow", !singleFiles);
7027
7028       // reset old skip state
7029       bool bSkip = false;
7030
7031       if (progress)
7032       {
7033         progress->SetLine(1, tvshow.m_strTitle);
7034         progress->SetPercentage(current * 100 / total);
7035         progress->Progress();
7036         if (progress->IsCanceled())
7037         {
7038           progress->Close();
7039           m_pDS->close();
7040           return;
7041         }
7042       }
7043
7044
7045       CFileItem item(tvshow.m_strPath, true);
7046       CFileItem saveItem(item);
7047       if (!singleFiles)
7048         saveItem = CFileItem(GetSafeFile(tvshowsDir, tvshow.m_strShowTitle), true);
7049       if (singleFiles && CUtil::SupportsFileOperations(tvshow.m_strPath))
7050       {
7051         if (!item.Exists(false))
7052         {
7053           CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
7054           bSkip = true;
7055         }
7056         else
7057         {
7058           CStdString nfoFile;
7059           URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo", nfoFile);
7060
7061           if (overwrite || !CFile::Exists(nfoFile, false))
7062           {
7063             if(!xmlDoc.SaveFile(nfoFile))
7064             {
7065               CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
7066               bSkip = ExportSkipEntry(nfoFile);
7067               if (!bSkip)
7068               {
7069                 if (progress)
7070                 {
7071                   progress->Close();
7072                   m_pDS->close();
7073                   return;
7074                 }
7075               }
7076             }
7077           }
7078
7079           xmlDoc.Clear();
7080           TiXmlDeclaration decl("1.0", "UTF-8", "yes");
7081           xmlDoc.InsertEndChild(decl);
7082         }
7083       }
7084       if (images && !bSkip)
7085       {
7086         CStdString cachedThumb(GetCachedThumb(item));
7087         CStdString savedThumb(saveItem.GetFolderThumb());
7088         if (!cachedThumb.IsEmpty() && (overwrite || !CFile::Exists(savedThumb, false)))
7089           if (!CFile::Cache(cachedThumb, savedThumb))
7090             CLog::Log(LOGERROR, "%s: TVShow thumb export failed! ('%s' -> '%s')", __FUNCTION__, cachedThumb.c_str(), savedThumb.c_str());
7091
7092         CStdString cachedFanart(item.GetCachedFanart());
7093         CStdString savedFanart(saveItem.GetFolderThumb("fanart.jpg"));
7094         if (CFile::Exists(cachedFanart, false) && (overwrite || !CFile::Exists(savedFanart, false)))
7095           if (!CFile::Cache(cachedFanart, savedFanart))
7096             CLog::Log(LOGERROR, "%s: TVShow fanart export failed! ('%s' -> '%s')", __FUNCTION__, cachedFanart.c_str(), savedFanart.c_str());
7097
7098         if (actorThumbs)
7099           ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
7100
7101         // now get all available seasons from this show
7102         sql = PrepareSQL("select distinct(c%02d) from episodeview where idShow=%i", VIDEODB_ID_EPISODE_SEASON, tvshow.m_iDbId);
7103         pDS2->query(sql.c_str());
7104
7105         CFileItemList items;
7106         CStdString strDatabasePath;
7107         strDatabasePath.Format("videodb://2/2/%i/",tvshow.m_iDbId);
7108
7109         // add "All Seasons" to list
7110         CFileItemPtr pItem;
7111         pItem.reset(new CFileItem(g_localizeStrings.Get(20366)));
7112         pItem->GetVideoInfoTag()->m_iSeason = -1;
7113         pItem->GetVideoInfoTag()->m_strPath = tvshow.m_strPath;
7114         items.Add(pItem);
7115
7116         // loop through available season
7117         while (!pDS2->eof())
7118         {
7119           int iSeason = pDS2->fv(0).get_asInt();
7120           CStdString strLabel;
7121           if (iSeason == 0)
7122             strLabel = g_localizeStrings.Get(20381);
7123           else
7124             strLabel.Format(g_localizeStrings.Get(20358),iSeason);
7125           CFileItemPtr pItem(new CFileItem(strLabel));
7126           pItem->GetVideoInfoTag()->m_strTitle = strLabel;
7127           pItem->GetVideoInfoTag()->m_iSeason = iSeason;
7128           pItem->GetVideoInfoTag()->m_strPath = tvshow.m_strPath;
7129           items.Add(pItem);
7130           pDS2->next();
7131         }
7132         pDS2->close();
7133
7134         // export season thumbs
7135         for (int i=0;i<items.Size();++i)
7136         {
7137           CStdString strSeasonThumb, strParent;
7138           int iSeason = items[i]->GetVideoInfoTag()->m_iSeason;
7139           if (iSeason == -1)
7140             strSeasonThumb = "season-all.tbn";
7141           else if (iSeason == 0)
7142             strSeasonThumb = "season-specials.tbn";
7143           else
7144             strSeasonThumb.Format("season%02i.tbn",iSeason);
7145
7146           CStdString cachedThumb(items[i]->GetCachedSeasonThumb());
7147           CStdString savedThumb(saveItem.GetFolderThumb(strSeasonThumb));
7148
7149           if (CFile::Exists(cachedThumb, false) && (overwrite || !CFile::Exists(savedThumb, false)))
7150             if (!CFile::Cache(cachedThumb, savedThumb))
7151               CLog::Log(LOGERROR, "%s: TVShow season thumb export failed ('%s' -> '%s')", __FUNCTION__, cachedThumb.c_str(), savedThumb.c_str());
7152         }
7153       }
7154       
7155       // now save the episodes from this show
7156       sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
7157       pDS->query(sql.c_str());
7158       CStdString showDir(saveItem.m_strPath);
7159
7160       while (!pDS->eof())
7161       {
7162         CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
7163         if (singleFiles)
7164           episode.Save(pMain, "episodedetails", !singleFiles);
7165         else
7166           episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
7167         pDS->next();
7168         // multi-episode files need dumping to the same XML
7169         while (singleFiles && !pDS->eof() &&
7170                episode.m_iFileId == pDS->fv("idFile").get_asInt())
7171         {
7172           episode = GetDetailsForEpisode(pDS, true);
7173           episode.Save(pMain, "episodedetails", !singleFiles);
7174           pDS->next();
7175         }
7176
7177         // reset old skip state
7178         bool bSkip = false;
7179
7180         CFileItem item(episode.m_strFileNameAndPath, false);
7181         CFileItem saveItem(item);
7182         if (!singleFiles)
7183         {
7184           CStdString epName;
7185           epName.Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
7186           saveItem = CFileItem(URIUtils::AddFileToFolder(showDir, epName), false);
7187         }
7188         if (singleFiles)
7189         {
7190           if (!item.Exists(false))
7191           {
7192             CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
7193             bSkip = true;
7194           }
7195           else
7196           {
7197             CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
7198
7199             if (overwrite || !CFile::Exists(nfoFile, false))
7200             {
7201               if(!xmlDoc.SaveFile(nfoFile))
7202               {
7203                 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
7204                 bSkip = ExportSkipEntry(nfoFile);
7205                 if (!bSkip)
7206                 {
7207                   if (progress)
7208                   {
7209                     progress->Close();
7210                     m_pDS->close();
7211                     return;
7212                   }
7213                 }
7214               }
7215             }
7216
7217             xmlDoc.Clear();
7218             TiXmlDeclaration decl("1.0", "UTF-8", "yes");
7219             xmlDoc.InsertEndChild(decl);
7220           }
7221         }
7222
7223         if (images && !bSkip)
7224         {
7225           CStdString cachedThumb(GetCachedThumb(item));
7226           CStdString savedThumb(saveItem.GetTBNFile());
7227           if (!cachedThumb.IsEmpty() && (overwrite || !CFile::Exists(savedThumb, false)))
7228             if (!CFile::Cache(cachedThumb, savedThumb))
7229               CLog::Log(LOGERROR, "%s: Episode thumb export failed! ('%s' -> '%s')", __FUNCTION__, cachedThumb.c_str(), savedThumb.c_str());
7230
7231           if (actorThumbs)
7232             ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
7233         }
7234       }
7235       pDS->close();
7236       m_pDS->next();
7237       current++;
7238     }
7239     m_pDS->close();
7240
7241     if (singleFiles && progress)
7242     {
7243       progress->SetPercentage(100);
7244       progress->Progress();
7245     }
7246
7247     if (!singleFiles)
7248     {
7249       // now dump path info
7250       set<CStdString> paths;
7251       GetPaths(paths);
7252       TiXmlElement xmlPathElement("paths");
7253       TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
7254       for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
7255       {
7256         bool foundDirectly = false;
7257         SScanSettings settings;
7258         ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
7259         if (info && foundDirectly)
7260         {
7261           TiXmlElement xmlPathElement2("path");
7262           TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
7263           XMLUtils::SetString(pPath,"url", *iter);
7264           XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
7265           XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
7266           XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
7267           XMLUtils::SetString(pPath,"scraperpath", info->ID());
7268         }
7269       }
7270       xmlDoc.SaveFile(xmlFile);
7271     }
7272   }
7273   catch (...)
7274   {
7275     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7276   }
7277
7278   if (progress)
7279     progress->Close();
7280 }
7281
7282 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
7283 {
7284   CStdString strPath(strDir);
7285   if (singleFiles)
7286   {
7287     strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
7288     if (!CDirectory::Exists(strPath))
7289     {
7290       CDirectory::Create(strPath);
7291       CFile::SetHidden(strPath, true);
7292     }
7293   }
7294
7295   for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
7296   {
7297     CFileItem item;
7298     item.SetLabel(iter->strName);
7299     CStdString strThumb = item.GetCachedActorThumb();
7300     if (CFile::Exists(strThumb))
7301     {
7302       CStdString thumbFile(GetSafeFile(strPath, iter->strName) + ".tbn");
7303       if (overwrite || !CFile::Exists(thumbFile))
7304         if (!CFile::Cache(strThumb, thumbFile))
7305           CLog::Log(LOGERROR, "%s: Actor thumb export failed! ('%s' -> '%s')", __FUNCTION__, strThumb.c_str(), thumbFile.c_str());
7306     }
7307   }
7308 }
7309
7310 CStdString CVideoDatabase::GetCachedThumb(const CFileItem& item) const
7311 {
7312   CStdString cachedThumb(item.GetCachedVideoThumb());
7313   if (!CFile::Exists(cachedThumb) && g_advancedSettings.m_bVideoLibraryExportAutoThumbs)
7314   {
7315     CStdString strPath, strFileName;
7316     URIUtils::Split(cachedThumb, strPath, strFileName);
7317     cachedThumb = strPath + "auto-" + strFileName;
7318   }
7319
7320   if (CFile::Exists(cachedThumb))
7321     return cachedThumb;
7322   else
7323     return "";
7324 }
7325
7326 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
7327 {
7328   CStdString strParent;
7329   URIUtils::GetParentPath(nfoFile,strParent);
7330   CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
7331
7332   bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
7333
7334   if (bSkip)
7335     CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
7336   else
7337     CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
7338
7339   return bSkip;
7340 }
7341
7342 void CVideoDatabase::ImportFromXML(const CStdString &path)
7343 {
7344   CGUIDialogProgress *progress=NULL;
7345   try
7346   {
7347     if (NULL == m_pDB.get()) return;
7348     if (NULL == m_pDS.get()) return;
7349
7350     TiXmlDocument xmlDoc;
7351     if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
7352       return;
7353
7354     TiXmlElement *root = xmlDoc.RootElement();
7355     if (!root) return;
7356
7357     progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
7358     if (progress)
7359     {
7360       progress->SetHeading(648);
7361       progress->SetLine(0, 649);
7362       progress->SetLine(1, 330);
7363       progress->SetLine(2, "");
7364       progress->SetPercentage(0);
7365       progress->StartModal();
7366       progress->ShowProgressBar(true);
7367     }
7368
7369     int iVersion = 0;
7370     XMLUtils::GetInt(root, "version", iVersion);
7371
7372     CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
7373
7374     TiXmlElement *movie = root->FirstChildElement();
7375     int current = 0;
7376     int total = 0;
7377     // first count the number of items...
7378     while (movie)
7379     {
7380       if (strnicmp(movie->Value(), "movie", 5)==0 ||
7381           strnicmp(movie->Value(), "tvshow", 6)==0 ||
7382           strnicmp(movie->Value(), "musicvideo",10)==0 )
7383         total++;
7384       movie = movie->NextSiblingElement();
7385     }
7386
7387     CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
7388     CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
7389     CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
7390     CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
7391     CVideoInfoScanner scanner;
7392     set<CStdString> actors;
7393     movie = root->FirstChildElement();
7394     while (movie)
7395     {
7396       CVideoInfoTag info;
7397       if (strnicmp(movie->Value(), "movie", 5) == 0)
7398       {
7399         info.Load(movie);
7400         CFileItem item(info);
7401         scanner.AddVideo(&item,CONTENT_MOVIES);
7402         SetPlayCount(item, info.m_playCount, info.m_lastPlayed);
7403         CStdString strFileName(info.m_strTitle);
7404         if (GetExportVersion() >= 1 && info.m_iYear > 0)
7405           strFileName.AppendFormat("_%i", info.m_iYear);
7406         CStdString file(GetSafeFile(moviesDir, strFileName));
7407         CFile::Cache(file + ".tbn", item.GetCachedVideoThumb());
7408         CFile::Cache(file + "-fanart.jpg", item.GetCachedFanart());
7409         for (CVideoInfoTag::iCast i = info.m_cast.begin(); i != info.m_cast.end(); ++i)
7410           actors.insert(i->strName);
7411         current++;
7412       }
7413       else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
7414       {
7415         info.Load(movie);
7416         CFileItem item(info);
7417         scanner.AddVideo(&item,CONTENT_MUSICVIDEOS);
7418         SetPlayCount(item, info.m_playCount, info.m_lastPlayed);
7419         CStdString strFileName(info.m_strArtist + "." + info.m_strTitle);
7420         if (GetExportVersion() >= 1 && info.m_iYear > 0)
7421           strFileName.AppendFormat("_%i", info.m_iYear);
7422         CStdString file(GetSafeFile(musicvideosDir, strFileName));
7423         CFile::Cache(file + ".tbn", item.GetCachedVideoThumb());
7424         current++;
7425       }
7426       else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
7427       {
7428         // load the TV show in.  NOTE: This deletes all episodes under the TV Show, which may not be
7429         // what we desire.  It may make better sense to only delete (or even better, update) the show information
7430         info.Load(movie);
7431         URIUtils::AddSlashAtEnd(info.m_strPath);
7432         DeleteTvShow(info.m_strPath);
7433         CFileItem item(info);
7434         int showID = scanner.AddVideo(&item,CONTENT_TVSHOWS);
7435         current++;
7436         CStdString showDir(GetSafeFile(tvshowsDir, info.m_strTitle));
7437         CFile::Cache(URIUtils::AddFileToFolder(showDir, "folder.jpg"), item.GetCachedVideoThumb());
7438         CFile::Cache(URIUtils::AddFileToFolder(showDir, "fanart.jpg"), item.GetCachedFanart());
7439         for (CVideoInfoTag::iCast i = info.m_cast.begin(); i != info.m_cast.end(); ++i)
7440           actors.insert(i->strName);
7441         // now load the episodes
7442         TiXmlElement *episode = movie->FirstChildElement("episodedetails");
7443         while (episode)
7444         {
7445           // no need to delete the episode info, due to the above deletion
7446           CVideoInfoTag info;
7447           info.Load(episode);
7448           CFileItem item(info);
7449           scanner.AddVideo(&item,CONTENT_TVSHOWS,false,showID);
7450           SetPlayCount(item, info.m_playCount, info.m_lastPlayed);
7451           CStdString file;
7452           file.Format("s%02ie%02i.tbn", info.m_iSeason, info.m_iEpisode);
7453           CFile::Cache(URIUtils::AddFileToFolder(showDir, file), item.GetCachedVideoThumb());
7454           for (CVideoInfoTag::iCast i = info.m_cast.begin(); i != info.m_cast.end(); ++i)
7455             actors.insert(i->strName);
7456           episode = episode->NextSiblingElement("episodedetails");
7457         }
7458         // and fetch season thumbs
7459         scanner.FetchSeasonThumbs(showID, showDir, false, true);
7460       }
7461       else if (strnicmp(movie->Value(), "paths", 5) == 0)
7462       {
7463         const TiXmlElement* path = movie->FirstChildElement("path");
7464         while (path)
7465         {
7466           CStdString strPath;
7467           XMLUtils::GetString(path,"url",strPath);
7468           CStdString content;
7469
7470           if (XMLUtils::GetString(path,"content", content))
7471           { // check the scraper exists, if so store the path
7472             AddonPtr addon;
7473             CStdString uuid;
7474
7475             if (!XMLUtils::GetString(path,"scraperID",uuid))
7476             { // support pre addons exports
7477               XMLUtils::GetString(path, "scraperpath", uuid);
7478               uuid = URIUtils::GetFileName(uuid);
7479             }
7480
7481             if (CAddonMgr::Get().GetAddon(uuid, addon))
7482             {
7483               SScanSettings settings;
7484               ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
7485               // FIXME: scraper settings are not exported?
7486               scraper->SetPathSettings(TranslateContent(content), "");
7487               XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
7488               XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
7489               SetScraperForPath(strPath,scraper,settings);
7490             }
7491           }
7492           path = path->NextSiblingElement();
7493         }
7494       }
7495       movie = movie->NextSiblingElement();
7496       if (progress && total)
7497       {
7498         progress->SetPercentage(current * 100 / total);
7499         progress->SetLine(2, info.m_strTitle);
7500         progress->Progress();
7501         if (progress->IsCanceled())
7502         {
7503           progress->Close();
7504           RollbackTransaction();
7505           return;
7506         }
7507       }
7508     }
7509     // cache any actor thumbs
7510     for (set<CStdString>::iterator i = actors.begin(); i != actors.end(); i++)
7511     {
7512       CFileItem item;
7513       item.SetLabel(*i);
7514       CStdString savedThumb(GetSafeFile(actorsDir, *i) + ".tbn");
7515       CStdString cachedThumb = item.GetCachedActorThumb();
7516       CFile::Cache(savedThumb, cachedThumb);
7517     }
7518   }
7519   catch (...)
7520   {
7521     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7522   }
7523   if (progress)
7524     progress->Close();
7525 }
7526
7527 bool CVideoDatabase::GetArbitraryQuery(const CStdString& strQuery, const CStdString& strOpenRecordSet, const CStdString& strCloseRecordSet,
7528                                        const CStdString& strOpenRecord, const CStdString& strCloseRecord, const CStdString& strOpenField, const CStdString& strCloseField, CStdString& strResult)
7529 {
7530   try
7531   {
7532     strResult = "";
7533     if (NULL == m_pDB.get()) return false;
7534     if (NULL == m_pDS.get()) return false;
7535     CStdString strSQL=strQuery;
7536     if (!m_pDS->query(strSQL.c_str()))
7537     {
7538       strResult = m_pDB->getErrorMsg();
7539       return false;
7540     }
7541     strResult=strOpenRecordSet;
7542     while (!m_pDS->eof())
7543     {
7544       strResult += strOpenRecord;
7545       for (int i=0; i<m_pDS->fieldCount(); i++)
7546       {
7547         strResult += strOpenField + CStdString(m_pDS->fv(i).get_asString()) + strCloseField;
7548       }
7549       strResult += strCloseRecord;
7550       m_pDS->next();
7551     }
7552     strResult += strCloseRecordSet;
7553     m_pDS->close();
7554     return true;
7555   }
7556   catch (...)
7557   {
7558     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7559   }
7560   try
7561   {
7562     if (NULL == m_pDB.get()) return false;
7563     strResult = m_pDB->getErrorMsg();
7564   }
7565   catch (...)
7566   {
7567
7568   }
7569
7570   return false;
7571 }
7572
7573 bool CVideoDatabase::ArbitraryExec(const CStdString& strExec)
7574 {
7575   try
7576   {
7577     if (NULL == m_pDB.get()) return false;
7578     if (NULL == m_pDS.get()) return false;
7579     CStdString strSQL = strExec;
7580     m_pDS->exec(strSQL.c_str());
7581     m_pDS->close();
7582     return true;
7583   }
7584   catch (...)
7585   {
7586     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7587   }
7588   return false;
7589 }
7590
7591 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
7592 {
7593   if (URIUtils::IsStack(strFileName) || URIUtils::IsInArchive(strFileName))
7594     strDest = strFileName;
7595   else
7596     URIUtils::AddFileToFolder(strPath, strFileName, strDest);
7597 }
7598
7599 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
7600 {
7601   if (URIUtils::IsStack(strFileNameAndPath) || strFileNameAndPath.Mid(0,6).Equals("rar://") || strFileNameAndPath.Mid(0,6).Equals("zip://"))
7602   {
7603     URIUtils::GetParentPath(strFileNameAndPath,strPath);
7604     strFileName = strFileNameAndPath;
7605   }
7606   else
7607     URIUtils::Split(strFileNameAndPath,strPath, strFileName);
7608 }
7609
7610 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
7611 {
7612   SScanSettings settings;
7613   bool foundDirectly;
7614   ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
7615   SetPathHash(strPath,"");
7616   if (!info)
7617     return;
7618   if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
7619   {
7620     if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
7621     {
7622       CStdString strParent;
7623       URIUtils::GetParentPath(strPath,strParent);
7624       SetPathHash(strParent,"");
7625     }
7626   }
7627 }
7628
7629 bool CVideoDatabase::CommitTransaction()
7630 {
7631   if (CDatabase::CommitTransaction())
7632   { // number of items in the db has likely changed, so recalculate
7633     g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
7634     g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
7635     g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
7636     return true;
7637   }
7638   return false;
7639 }
7640
7641 void CVideoDatabase::DeleteThumbForItem(const CStdString& strPath, bool bFolder, int idEpisode)
7642 {
7643   CFileItem item(strPath,bFolder);
7644   if (idEpisode > 0)
7645   {
7646     item.m_strPath = item.GetVideoInfoTag()->m_strFileNameAndPath;
7647     if (CFile::Exists(item.GetCachedEpisodeThumb()))
7648       CTextureCache::Get().ClearCachedImage(item.GetCachedEpisodeThumb(), true);
7649     else
7650       CTextureCache::Get().ClearCachedImage(item.GetCachedVideoThumb(), true);
7651   }
7652   else
7653   {
7654     CTextureCache::Get().ClearCachedImage(item.GetCachedVideoThumb(), true);
7655     CTextureCache::Get().ClearCachedImage(item.GetCachedFanart(), true);
7656   }
7657
7658   // tell our GUI to completely reload all controls (as some of them
7659   // are likely to have had this image in use so will need refreshing)
7660   CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
7661   g_windowManager.SendThreadMessage(msg);
7662 }
7663
7664 void CVideoDatabase::SetDetail(const CStdString& strDetail, int id, int field,
7665                                VIDEODB_CONTENT_TYPE type)
7666 {
7667   try
7668   {
7669     if (NULL == m_pDB.get()) return;
7670     if (NULL == m_pDS.get()) return;
7671
7672     CStdString strTable, strField;
7673     if (type == VIDEODB_CONTENT_MOVIES)
7674     {
7675       strTable = "movie";
7676       strField = "idMovie";
7677     }
7678     if (type == VIDEODB_CONTENT_TVSHOWS)
7679     {
7680       strTable = "tvshow";
7681       strField = "idShow";
7682     }
7683     if (type == VIDEODB_CONTENT_MUSICVIDEOS)
7684     {
7685       strTable = "musicvideo";
7686       strField = "idMVideo";
7687     }
7688
7689     if (strTable.IsEmpty())
7690       return;
7691
7692     CStdString strSQL = PrepareSQL("update %s set c%02u='%s' where %s=%u",
7693                                   strTable.c_str(), field, strDetail.c_str(), strField.c_str(), id);
7694     m_pDS->exec(strSQL.c_str());
7695   }
7696   catch (...)
7697   {
7698     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7699   }
7700 }
7701
7702 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
7703 {
7704   CStdString safeThumb(name);
7705   safeThumb.Replace(' ', '_');
7706   return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
7707 }
7708
7709 void CVideoDatabase::AnnounceRemove(std::string content, int id)
7710 {
7711   CVariant data;
7712   data["content"] = content;
7713   data[content + "id"] = id;
7714   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Library, "xbmc", "RemoveVideo", data);
7715 }
7716
7717 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
7718 {
7719   CVariant data;
7720   data["content"] = content;
7721   data[content + "id"] = id;
7722   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Library, "xbmc", "UpdateVideo", data);
7723 }
7724
7725 bool CVideoDatabase::GetItemForPath(const CStdString &content, const CStdString &strPath, CFileItem &item)
7726 {
7727   CFileItemList items;
7728   CStdString path(strPath);
7729
7730   if(URIUtils::IsMultiPath(path))
7731     path = CMultiPathDirectory::GetFirstPath(path);
7732
7733   if (content == "movies")
7734   {
7735     CStdString where = PrepareSQL("where c%02d='%s' limit 1", VIDEODB_ID_BASEPATH, path.c_str());
7736     GetMoviesByWhere("", where, "", items);
7737   }
7738   else if (content == "episodes")
7739   {
7740     CStdString where = PrepareSQL("where c%02d='%s' limit 1", VIDEODB_ID_EPISODE_BASEPATH, path.c_str());
7741     GetEpisodesByWhere("", where, items);
7742   }
7743   else if (content == "tvshows")
7744   {
7745     CStdString where = PrepareSQL("where c%02d='%s' limit 1", VIDEODB_ID_TV_BASEPATH, path.c_str());
7746     GetTvShowsByWhere("", where, items);
7747   }
7748   else if (content == "musicvideos")
7749   {
7750     CStdString where = PrepareSQL("where c%02d='%s' limit 1", VIDEODB_ID_MUSICVIDEO_BASEPATH, path.c_str());
7751     GetMusicVideosByWhere("", where, items);
7752   }
7753   if (items.Size())
7754   {
7755     item = *items[0];
7756     if (item.m_bIsFolder)
7757       item.m_strPath = item.GetVideoInfoTag()->m_strPath;
7758     else
7759       item.m_strPath = item.GetVideoInfoTag()->m_strFileNameAndPath;
7760     return true;
7761   }
7762   return false;
7763 }