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