fixed: music videos album node had an invalid sql query.
[xbmc:xbmc-antiquated.git] / xbmc / 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 "GUIWindowVideoBase.h"
24 #include "utils/RegExp.h"
25 #include "addons/AddonManager.h"
26 #include "utils/GUIInfoManager.h"
27 #include "Util.h"
28 #include "URIUtils.h"
29 #include "XMLUtils.h"
30 #include "GUIPassword.h"
31 #include "FileSystem/StackDirectory.h"
32 #include "FileSystem/MultiPathDirectory.h"
33 #include "VideoInfoScanner.h"
34 #include "GUIWindowManager.h"
35 #include "FileSystem/Directory.h"
36 #include "FileSystem/File.h"
37 #include "FileSystem/SpecialProtocol.h"
38 #include "GUIDialogProgress.h"
39 #include "GUIDialogYesNo.h"
40 #include "FileItem.h"
41 #include "AdvancedSettings.h"
42 #include "GUISettings.h"
43 #include "Settings.h"
44 #include "StringUtils.h"
45 #include "LocalizeStrings.h"
46 #include "utils/TimeUtils.h"
47 #include "utils/log.h"
48 #include "TextureCache.h"
49 #include "GUIWindowAddonBrowser.h"
50 #include "utils/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 #define VIDEO_DATABASE_VIEW_TVSHOW "SELECT tvshow.*,path.strPath AS strPath," \
59                                    "counts.totalcount AS totalCount,counts.watchedcount AS watchedCount," \
60                                    "counts.totalcount=counts.watchedcount AS watched FROM tvshow " \
61                                    "JOIN tvshowlinkpath ON tvshow.idShow=tvshowlinkpath.idShow " \
62                                    "JOIN path ON path.idpath=tvshowlinkpath.idPath " \
63                                    "LEFT OUTER join (" \
64                                    "    SELECT tvshow.idShow AS idShow,count(1) AS totalcount,count(files.playCount) AS watchedcount FROM tvshow " \
65                                    "    JOIN tvshowlinkepisode ON tvshow.idShow=tvshowlinkepisode.idShow " \
66                                    "    JOIN episode ON episode.idEpisode=tvshowlinkepisode.idEpisode " \
67                                    "    JOIN files ON files.idFile=episode.idFile " \
68                                    "    GROUP BY tvshow.idShow" \
69                                    ") counts ON tvshow.idShow=counts.idShow "
70
71 //********************************************************************************************************************************
72 CVideoDatabase::CVideoDatabase(void)
73 {
74 }
75
76 //********************************************************************************************************************************
77 CVideoDatabase::~CVideoDatabase(void)
78 {}
79
80 //********************************************************************************************************************************
81 bool CVideoDatabase::Open()
82 {
83   return CDatabase::Open(g_advancedSettings.m_databaseVideo);
84 }
85
86 bool CVideoDatabase::CreateTables()
87 {
88   /* indexes should be added on any columns that are used in in  */
89   /* a where or a join. primary key on a column is the same as a */
90   /* unique index on that column, so there is no need to add any */
91   /* index if no other columns are refered                       */
92
93   /* order of indexes are important, for an index to be considered all  */
94   /* columns up to the column in question have to have been specified   */
95   /* select * from actorlinkmovie where idMovie = 1, can not take       */
96   /* advantage of a index that has been created on ( idGenre, idMovie ) */
97   /*, hower on on ( idMovie, idGenre ) will be considered for use       */
98
99   BeginTransaction();
100   try
101   {
102     CDatabase::CreateTables();
103
104     CLog::Log(LOGINFO, "create bookmark table");
105     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");
106     m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile)");
107
108     CLog::Log(LOGINFO, "create settings table");
109     m_pDS->exec("CREATE TABLE settings ( idFile integer, Deinterlace bool,"
110                 "ViewMode integer,ZoomAmount float, PixelRatio float, AudioStream integer, SubtitleStream integer,"
111                 "SubtitleDelay float, SubtitlesOn bool, Brightness float, Contrast float, Gamma float,"
112                 "VolumeAmplification float, AudioDelay float, OutputToAllSpeakers bool, ResumeTime integer, Crop bool, CropLeft integer,"
113                 "CropRight integer, CropTop integer, CropBottom integer, Sharpness float, NoiseReduction float, NonLinStretch bool, PostProcess bool)\n");
114     m_pDS->exec("CREATE UNIQUE INDEX ix_settings ON settings ( idFile )\n");
115
116     CLog::Log(LOGINFO, "create stacktimes table");
117     m_pDS->exec("CREATE TABLE stacktimes (idFile integer, times text)\n");
118     m_pDS->exec("CREATE UNIQUE INDEX ix_stacktimes ON stacktimes ( idFile )\n");
119
120     CLog::Log(LOGINFO, "create genre table");
121     m_pDS->exec("CREATE TABLE genre ( idGenre integer primary key, strGenre text)\n");
122
123     CLog::Log(LOGINFO, "create genrelinkmovie table");
124     m_pDS->exec("CREATE TABLE genrelinkmovie ( idGenre integer, idMovie integer)\n");
125     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_1 ON genrelinkmovie ( idGenre, idMovie)\n");
126     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_2 ON genrelinkmovie ( idMovie, idGenre)\n");
127
128     CLog::Log(LOGINFO, "create country table");
129     m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
130
131     CLog::Log(LOGINFO, "create countrylinkmovie table");
132     m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
133     m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
134     m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
135
136     CLog::Log(LOGINFO, "create movie table");
137     CStdString columns = "CREATE TABLE movie ( idMovie integer primary key, idFile integer";
138     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
139     {
140       CStdString column;
141       column.Format(",c%02d text", i);
142       columns += column;
143     }
144     columns += ")";
145     m_pDS->exec(columns.c_str());
146     m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
147     m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
148
149     CLog::Log(LOGINFO, "create actorlinkmovie table");
150     m_pDS->exec("CREATE TABLE actorlinkmovie ( idActor integer, idMovie integer, strRole text)\n");
151     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_1 ON actorlinkmovie ( idActor, idMovie )\n");
152     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_2 ON actorlinkmovie ( idMovie, idActor )\n");
153
154     CLog::Log(LOGINFO, "create directorlinkmovie table");
155     m_pDS->exec("CREATE TABLE directorlinkmovie ( idDirector integer, idMovie integer)\n");
156     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_1 ON directorlinkmovie ( idDirector, idMovie )\n");
157     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_2 ON directorlinkmovie ( idMovie, idDirector )\n");
158
159     CLog::Log(LOGINFO, "create writerlinkmovie table");
160     m_pDS->exec("CREATE TABLE writerlinkmovie ( idWriter integer, idMovie integer)\n");
161     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_1 ON writerlinkmovie ( idWriter, idMovie )\n");
162     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_2 ON writerlinkmovie ( idMovie, idWriter )\n");
163
164     CLog::Log(LOGINFO, "create actors table");
165     m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
166
167     CLog::Log(LOGINFO, "create path table");
168     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)");
169     m_pDS->exec("CREATE UNIQUE INDEX ix_path ON path ( strPath(255) )");
170
171     CLog::Log(LOGINFO, "create files table");
172     m_pDS->exec("CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text)");
173     m_pDS->exec("CREATE UNIQUE INDEX ix_files ON files ( idPath, strFilename(255) )");
174
175     CLog::Log(LOGINFO, "create tvshow table");
176     columns = "CREATE TABLE tvshow ( idShow integer primary key";
177     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
178     {
179       CStdString column;
180       column.Format(",c%02d text", i);
181       columns += column;
182     }
183     columns += ")";
184     m_pDS->exec(columns.c_str());
185
186     CLog::Log(LOGINFO, "create directorlinktvshow table");
187     m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
188     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
189     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
190
191     CLog::Log(LOGINFO, "create actorlinktvshow table");
192     m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text)\n");
193     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
194     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
195
196     CLog::Log(LOGINFO, "create studiolinktvshow table");
197     m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
198     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
199     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
200
201     CLog::Log(LOGINFO, "create episode table");
202     columns = "CREATE TABLE episode ( idEpisode integer primary key, idFile integer";
203     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
204     {
205       CStdString column;
206       if ( i == VIDEODB_ID_EPISODE_SEASON || i == VIDEODB_ID_EPISODE_EPISODE || i == VIDEODB_ID_EPISODE_BOOKMARK)
207         column.Format(",c%02d varchar(24)", i);
208       else
209         column.Format(",c%02d text", i);
210
211       columns += column;
212     }
213     columns += ")";
214     m_pDS->exec(columns.c_str());
215     m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
216     m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
217     CStdString createColIndex;
218     createColIndex.Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
219     m_pDS->exec(createColIndex.c_str());
220     createColIndex.Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
221     m_pDS->exec(createColIndex.c_str());
222
223     CLog::Log(LOGINFO, "create tvshowlinkepisode table");
224     m_pDS->exec("CREATE TABLE tvshowlinkepisode ( idShow integer, idEpisode integer)\n");
225     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkepisode_1 ON tvshowlinkepisode ( idShow, idEpisode )\n");
226     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkepisode_2 ON tvshowlinkepisode ( idEpisode, idShow )\n");
227
228     CLog::Log(LOGINFO, "create tvshowlinkpath table");
229     m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
230     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
231     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
232
233     CLog::Log(LOGINFO, "create actorlinkepisode table");
234     m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text)\n");
235     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
236     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
237
238     CLog::Log(LOGINFO, "create directorlinkepisode table");
239     m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
240     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
241     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
242
243     CLog::Log(LOGINFO, "create writerlinkepisode table");
244     m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
245     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
246     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
247
248     CLog::Log(LOGINFO, "create genrelinktvshow table");
249     m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
250     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
251     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
252
253     CLog::Log(LOGINFO, "create movielinktvshow table");
254     m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
255     m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
256     m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
257
258     CLog::Log(LOGINFO, "create studio table");
259     m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
260
261     CLog::Log(LOGINFO, "create studiolinkmovie table");
262     m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
263     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
264     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
265
266     CLog::Log(LOGINFO, "create musicvideo table");
267     columns = "CREATE TABLE musicvideo ( idMVideo integer primary key, idFile integer";
268     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
269     {
270       CStdString column;
271       column.Format(",c%02d text", i);
272       columns += column;
273     }
274     columns += ")";
275     m_pDS->exec(columns.c_str());
276     m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
277     m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
278
279     CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
280     m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
281     m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
282     m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
283
284     CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
285     m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
286     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
287     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
288
289     CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
290     m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
291     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
292     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
293
294     CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
295     m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
296     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
297     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
298
299     CLog::Log(LOGINFO, "create streaminfo table");
300     m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
301       "strVideoCodec text, fVideoAspect float, iVideoWidth integer, iVideoHeight integer, "
302       "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text, iVideoDuration integer)");
303     m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
304
305     CLog::Log(LOGINFO, "create episodeview");
306     CStdString episodeview = PrepareSQL("create view episodeview as select episode.*,files.strFileName as strFileName,"
307                                        "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,"
308                                        "tvshow.c%02d as premiered, tvshow.c%02d as mpaa from episode "
309                                        "join files on files.idFile=episode.idFile "
310                                        "join tvshowlinkepisode on episode.idepisode=tvshowlinkepisode.idEpisode "
311                                        "join tvshow on tvshow.idShow=tvshowlinkepisode.idShow "
312                                        "join path on files.idPath=path.idPath",VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_PREMIERED, VIDEODB_ID_TV_MPAA);
313     m_pDS->exec(episodeview.c_str());
314
315     CLog::Log(LOGINFO, "create musicvideoview");
316     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 "
317                 "from musicvideo join files on files.idFile=musicvideo.idFile join path on path.idPath=files.idPath");
318
319     CLog::Log(LOGINFO, "create movieview");
320     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 "
321                 "from movie join files on files.idFile=movie.idFile join path on path.idPath=files.idPath");
322
323     CLog::Log(LOGINFO, "create sets table");
324     m_pDS->exec("CREATE TABLE sets ( idSet integer primary key, strSet text)\n");
325
326     CLog::Log(LOGINFO, "create setlinkmovie table");
327     m_pDS->exec("CREATE TABLE setlinkmovie ( idSet integer, idMovie integer)\n");
328     m_pDS->exec("CREATE UNIQUE INDEX ix_setlinkmovie_1 ON setlinkmovie ( idSet, idMovie)\n");
329     m_pDS->exec("CREATE UNIQUE INDEX ix_setlinkmovie_2 ON setlinkmovie ( idMovie, idSet)\n");
330   }
331   catch (...)
332   {
333     CLog::Log(LOGERROR, "%s unable to create tables:%i", __FUNCTION__, (int)GetLastError());
334     RollbackTransaction();
335     return false;
336   }
337   CommitTransaction();
338   return true;
339 }
340
341 //********************************************************************************************************************************
342 int CVideoDatabase::GetPathId(const CStdString& strPath)
343 {
344   CStdString strSQL;
345   try
346   {
347     int idPath=-1;
348     if (NULL == m_pDB.get()) return -1;
349     if (NULL == m_pDS.get()) return -1;
350
351     CStdString strPath1(strPath);
352     if (CUtil::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
353       CUtil::GetParentPath(strPath,strPath1);
354
355     CUtil::AddSlashAtEnd(strPath1);
356
357     strSQL=PrepareSQL("select idPath from path where strPath like '%s'",strPath1.c_str());
358     m_pDS->query(strSQL.c_str());
359     if (!m_pDS->eof())
360       idPath = m_pDS->fv("path.idPath").get_asInt();
361
362     m_pDS->close();
363     return idPath;
364   }
365   catch (...)
366   {
367     CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
368   }
369   return -1;
370 }
371
372 bool CVideoDatabase::GetPaths(set<CStdString> &paths)
373 {
374   try
375   {
376     if (NULL == m_pDB.get()) return false;
377     if (NULL == m_pDS.get()) return false;
378
379     paths.clear();
380
381     // grab all paths with movie content set
382     if (!m_pDS->query("select strPath,noUpdate from path"
383                       " where (strContent = 'movies' or strContent = 'musicvideos')"
384                       " and strPath NOT like 'multipath://%%'"
385                       " order by strPath"))
386       return false;
387
388     while (!m_pDS->eof())
389     {
390       if (!m_pDS->fv("noUpdate").get_asBool())
391         paths.insert(m_pDS->fv("strPath").get_asString());
392       m_pDS->next();
393     }
394     m_pDS->close();
395
396     // then grab all tvshow paths
397     if (!m_pDS->query("select strPath,noUpdate from path"
398                       " where ( strContent = 'tvshows'"
399                       "       or idPath in (select idPath from tvshowlinkpath))"
400                       " and strPath NOT like 'multipath://%%'"
401                       " order by strPath"))
402       return false;
403
404     while (!m_pDS->eof())
405     {
406       if (!m_pDS->fv("noUpdate").get_asBool())
407         paths.insert(m_pDS->fv("strPath").get_asString());
408       m_pDS->next();
409     }
410     m_pDS->close();
411
412     // finally grab all other paths holding a movie which is not a stack or a rar archive
413     // - this isnt perfect but it should do fine in most situations.
414     // reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
415     // not making mistakes must take priority
416     if (!m_pDS->query("select strPath,noUpdate from path"
417                        " where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
418                        " and idPath NOT in (select idPath from tvshowlinkpath)"
419                        " 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
420                        " and idPath NOT in (select idPath from files where strFileName like 'index.bdmv')" // bluray folders get stacked to a single item in parent folder
421                        " and strPath NOT like 'multipath://%%'"
422                        " and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
423                        " order by strPath"))
424
425       return false;
426     while (!m_pDS->eof())
427     {
428       if (!m_pDS->fv("noUpdate").get_asBool())
429         paths.insert(m_pDS->fv("strPath").get_asString());
430       m_pDS->next();
431     }
432     m_pDS->close();
433     return true;
434   }
435   catch (...)
436   {
437     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
438   }
439   return false;
440 }
441
442 bool CVideoDatabase::GetPathsForTvShow(int idShow, vector<int>& paths)
443 {
444   CStdString strSQL;
445   try
446   {
447     if (NULL == m_pDB.get()) return false;
448     if (NULL == m_pDS.get()) return false;
449     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);
450     m_pDS->query(strSQL.c_str());
451     while (!m_pDS->eof())
452     {
453       paths.push_back(m_pDS->fv(0).get_asInt());
454       m_pDS->next();
455     }
456     m_pDS->close();
457     return true;
458   }
459   catch (...)
460   {
461     CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
462   }
463   return false;
464 }
465
466 int CVideoDatabase::AddPath(const CStdString& strPath)
467 {
468   CStdString strSQL;
469   try
470   {
471     int idPath;
472     if (NULL == m_pDB.get()) return -1;
473     if (NULL == m_pDS.get()) return -1;
474
475     CStdString strPath1(strPath);
476     if (CUtil::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
477       CUtil::GetParentPath(strPath,strPath1);
478
479     CUtil::AddSlashAtEnd(strPath1);
480
481     strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
482     m_pDS->exec(strSQL.c_str());
483     idPath = (int)m_pDS->lastinsertid();
484     return idPath;
485   }
486   catch (...)
487   {
488     CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
489   }
490   return -1;
491 }
492
493 bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
494 {
495   try
496   {
497     if (NULL == m_pDB.get()) return false;
498     if (NULL == m_pDS.get()) return false;
499
500     CStdString strSQL=PrepareSQL("select strHash from path where strPath like '%s'", path.c_str());
501     m_pDS->query(strSQL.c_str());
502     if (m_pDS->num_rows() == 0)
503       return false;
504     hash = m_pDS->fv("strHash").get_asString();
505     return true;
506   }
507   catch (...)
508   {
509     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
510   }
511
512   return false;
513 }
514
515 //********************************************************************************************************************************
516 int CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
517 {
518   CStdString strSQL = "";
519   try
520   {
521     int idFile;
522     if (NULL == m_pDB.get()) return -1;
523     if (NULL == m_pDS.get()) return -1;
524
525     CStdString strFileName, strPath;
526     SplitPath(strFileNameAndPath,strPath,strFileName);
527
528     int idPath=GetPathId(strPath);
529     if (idPath < 0)
530       idPath = AddPath(strPath);
531
532     if (idPath < 0)
533       return -1;
534
535     CStdString strSQL=PrepareSQL("select idFile from files where strFileName like '%s' and idPath=%i", strFileName.c_str(),idPath);
536
537     m_pDS->query(strSQL.c_str());
538     if (m_pDS->num_rows() > 0)
539     {
540       idFile = m_pDS->fv("idFile").get_asInt() ;
541       m_pDS->close();
542       return idFile;
543     }
544     m_pDS->close();
545     strSQL=PrepareSQL("insert into files (idFile,idPath,strFileName) values(NULL, %i, '%s')", idPath,strFileName.c_str());
546     m_pDS->exec(strSQL.c_str());
547     idFile = (int)m_pDS->lastinsertid();
548     return idFile;
549   }
550   catch (...)
551   {
552     CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
553   }
554   return -1;
555 }
556
557 int CVideoDatabase::AddFile(const CFileItem& item)
558 {
559   if (item.IsVideoDb() && item.HasVideoInfoTag())
560     return AddFile(item.GetVideoInfoTag()->m_strFileNameAndPath);
561   return AddFile(item.m_strPath);
562 }
563
564 bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
565 {
566   try
567   {
568     if (NULL == m_pDB.get()) return false;
569     if (NULL == m_pDS.get()) return false;
570
571     if (hash.IsEmpty())
572     { // this is an empty folder - we need only add it to the path table
573       // if the path actually exists
574       if (!CDirectory::Exists(path))
575         return false;
576     }
577     int idPath = GetPathId(path);
578     if (idPath < 0)
579       idPath = AddPath(path);
580     if (idPath < 0) return false;
581
582     CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
583     m_pDS->exec(strSQL.c_str());
584
585     return true;
586   }
587   catch (...)
588   {
589     CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
590   }
591
592   return false;
593 }
594
595 bool CVideoDatabase::LinkMovieToTvshow(int idMovie, int idShow, bool bRemove)
596 {
597    try
598   {
599     if (NULL == m_pDB.get()) return false;
600     if (NULL == m_pDS.get()) return false;
601
602     if (bRemove) // delete link
603     {
604       CStdString strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i and idShow=%i", idMovie, idShow);
605       m_pDS->exec(strSQL.c_str());
606       return true;
607     }
608
609     CStdString strSQL=PrepareSQL("insert into movielinktvshow (idShow,idMovie) values (%i,%i)", idShow,idMovie);
610     m_pDS->exec(strSQL.c_str());
611
612     return true;
613   }
614   catch (...)
615   {
616     CLog::Log(LOGERROR, "%s (%i, %i) failed", __FUNCTION__, idMovie, idShow);
617   }
618
619   return false;
620 }
621
622 bool CVideoDatabase::IsLinkedToTvshow(int idMovie)
623 {
624    try
625   {
626     if (NULL == m_pDB.get()) return false;
627     if (NULL == m_pDS.get()) return false;
628
629     CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
630     m_pDS->query(strSQL.c_str());
631     if (m_pDS->eof())
632     {
633       m_pDS->close();
634       return false;
635     }
636
637     m_pDS->close();
638     return true;
639   }
640   catch (...)
641   {
642     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
643   }
644
645   return false;
646 }
647
648 bool CVideoDatabase::GetLinksToTvShow(int idMovie, vector<int>& ids)
649 {
650    try
651   {
652     if (NULL == m_pDB.get()) return false;
653     if (NULL == m_pDS.get()) return false;
654
655     CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
656     m_pDS2->query(strSQL.c_str());
657     while (!m_pDS2->eof())
658     {
659       ids.push_back(m_pDS2->fv(1).get_asInt());
660       m_pDS2->next();
661     }
662
663     m_pDS2->close();
664     return true;
665   }
666   catch (...)
667   {
668     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
669   }
670
671   return false;
672 }
673
674
675 //********************************************************************************************************************************
676 int CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
677 {
678   try
679   {
680     if (NULL == m_pDB.get()) return -1;
681     if (NULL == m_pDS.get()) return -1;
682     CStdString strPath, strFileName;
683     SplitPath(strFilenameAndPath,strPath,strFileName);
684
685     int idPath = GetPathId(strPath);
686     if (idPath >= 0)
687     {
688       CStdString strSQL;
689       strSQL=PrepareSQL("select idFile from files where strFileName like '%s' and idPath=%i", strFileName.c_str(),idPath);
690       m_pDS->query(strSQL.c_str());
691       if (m_pDS->num_rows() > 0)
692       {
693         int idFile = m_pDS->fv("files.idFile").get_asInt();
694         m_pDS->close();
695         return idFile;
696       }
697     }
698   }
699   catch (...)
700   {
701     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
702   }
703   return -1;
704 }
705
706 int CVideoDatabase::GetFileId(const CFileItem &item)
707 {
708   if (item.IsVideoDb() && item.HasVideoInfoTag())
709     return GetFileId(item.GetVideoInfoTag()->m_strFileNameAndPath);
710   return GetFileId(item.m_strPath);
711 }
712
713 //********************************************************************************************************************************
714 int CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
715 {
716   try
717   {
718     if (NULL == m_pDB.get()) return -1;
719     if (NULL == m_pDS.get()) return -1;
720     int idMovie = -1;
721
722     // needed for query parameters
723     int idFile = GetFileId(strFilenameAndPath);
724     int idPath=-1;
725     CStdString strPath;
726     if (idFile < 0)
727     {
728       CStdString strFile;
729       SplitPath(strFilenameAndPath,strPath,strFile);
730
731       // have to join movieinfo table for correct results
732       idPath = GetPathId(strPath);
733       if (idPath < 0 && strPath != strFilenameAndPath)
734         return -1;
735     }
736
737     if (idFile == -1 && strPath != strFilenameAndPath)
738       return -1;
739
740     CStdString strSQL;
741     if (idFile == -1)
742       strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
743     else
744       strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
745
746     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
747     m_pDS->query(strSQL.c_str());
748     if (m_pDS->num_rows() > 0)
749       idMovie = m_pDS->fv("idMovie").get_asInt();
750     m_pDS->close();
751
752     return idMovie;
753   }
754   catch (...)
755   {
756     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
757   }
758   return -1;
759 }
760
761 int CVideoDatabase::GetTvShowId(const CStdString& strPath)
762 {
763   try
764   {
765     if (NULL == m_pDB.get()) return -1;
766     if (NULL == m_pDS.get()) return -1;
767     int idTvShow = -1;
768
769     // have to join movieinfo table for correct results
770     int idPath = GetPathId(strPath);
771     if (idPath < 0)
772       return -1;
773
774     CStdString strSQL;
775     CStdString strPath1=strPath;
776     CStdString strParent;
777     int iFound=0;
778
779     strSQL=PrepareSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%i",idPath);
780     m_pDS->query(strSQL);
781     if (!m_pDS->eof())
782       iFound = 1;
783
784     while (iFound == 0 && CUtil::GetParentPath(strPath1, strParent))
785     {
786       strSQL=PrepareSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and strPath like '%s'",strParent.c_str());
787       m_pDS->query(strSQL.c_str());
788       if (!m_pDS->eof())
789       {
790         int idShow = m_pDS->fv("idShow").get_asInt();
791         if (idShow != -1)
792           iFound = 2;
793       }
794       strPath1 = strParent;
795     }
796
797     if (m_pDS->num_rows() > 0)
798       idTvShow = m_pDS->fv("idShow").get_asInt();
799     m_pDS->close();
800
801     return idTvShow;
802   }
803   catch (...)
804   {
805     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
806   }
807   return -1;
808 }
809
810 int CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, int idEpisode, int idSeason) // input value is episode/season number hint - for multiparters
811 {
812   try
813   {
814     if (NULL == m_pDB.get()) return -1;
815     if (NULL == m_pDS.get()) return -1;
816
817     // need this due to the nested GetEpisodeInfo query
818     auto_ptr<Dataset> pDS;
819     pDS.reset(m_pDB->CreateDataset());
820     if (NULL == pDS.get()) return -1;
821
822     int idFile = GetFileId(strFilenameAndPath);
823     if (idFile < 0)
824       return -1;
825
826     CStdString strSQL=PrepareSQL("select idEpisode from episode where idFile=%i", idFile);
827
828     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
829     pDS->query(strSQL.c_str());
830     if (pDS->num_rows() > 0)
831     {
832       if (idEpisode == -1)
833         idEpisode = pDS->fv("episode.idEpisode").get_asInt();
834       else // use the hint!
835       {
836         while (!pDS->eof())
837         {
838           CVideoInfoTag tag;
839           int idTmpEpisode = pDS->fv("episode.idEpisode").get_asInt();
840           GetEpisodeInfo(strFilenameAndPath,tag,idTmpEpisode);
841           if (tag.m_iEpisode == idEpisode && (idSeason == -1 || tag.m_iSeason == idSeason)) {
842             // match on the episode hint, and there's no season hint or a season hint match
843             idEpisode = idTmpEpisode;
844             break;
845           }
846           pDS->next();
847         }
848         if (pDS->eof())
849           idEpisode = -1;
850       }
851     }
852     else
853       idEpisode = -1;
854
855     pDS->close();
856
857     return idEpisode;
858   }
859   catch (...)
860   {
861     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
862   }
863   return -1;
864 }
865
866 int CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
867 {
868   try
869   {
870     if (NULL == m_pDB.get()) return -1;
871     if (NULL == m_pDS.get()) return -1;
872
873     int idFile = GetFileId(strFilenameAndPath);
874     if (idFile < 0)
875       return -1;
876
877     CStdString strSQL=PrepareSQL("select idMVideo from musicvideo where idFile=%i", idFile);
878
879     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
880     m_pDS->query(strSQL.c_str());
881     int idMVideo=-1;
882     if (m_pDS->num_rows() > 0)
883       idMVideo = m_pDS->fv("idMVideo").get_asInt();
884     m_pDS->close();
885
886     return idMVideo;
887   }
888   catch (...)
889   {
890     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
891   }
892   return -1;
893 }
894
895 //********************************************************************************************************************************
896 int CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
897 {
898   try
899   {
900     if (NULL == m_pDB.get()) return -1;
901     if (NULL == m_pDS.get()) return -1;
902
903     int idMovie = GetMovieId(strFilenameAndPath);
904     if (idMovie < 0)
905     {
906       int idFile = AddFile(strFilenameAndPath);
907       if (idFile < 0)
908         return -1;
909       CStdString strSQL=PrepareSQL("insert into movie (idMovie, idFile) values (NULL, %i)", idFile);
910       m_pDS->exec(strSQL.c_str());
911       idMovie = (int)m_pDS->lastinsertid();
912 //      CommitTransaction();
913     }
914
915     return idMovie;
916   }
917   catch (...)
918   {
919     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
920   }
921   return -1;
922 }
923
924 int CVideoDatabase::AddTvShow(const CStdString& strPath)
925 {
926   try
927   {
928     if (NULL == m_pDB.get()) return -1;
929     if (NULL == m_pDS.get()) return -1;
930
931     CStdString strSQL=PrepareSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath like '%s' and path.idPath=tvshowlinkpath.idPath",strPath.c_str());
932     m_pDS->query(strSQL.c_str());
933     if (m_pDS->num_rows() != 0)
934       return m_pDS->fv("tvshowlinkpath.idShow").get_asInt();
935
936     strSQL=PrepareSQL("insert into tvshow (idShow) values (NULL)");
937     m_pDS->exec(strSQL.c_str());
938     int idTvShow = (int)m_pDS->lastinsertid();
939
940     int idPath = GetPathId(strPath);
941     if (idPath < 0)
942       idPath = AddPath(strPath);
943     strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
944     m_pDS->exec(strSQL.c_str());
945
946 //    CommitTransaction();
947
948     return idTvShow;
949   }
950   catch (...)
951   {
952     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
953   }
954   return -1;
955 }
956
957 //********************************************************************************************************************************
958 int CVideoDatabase::AddEpisode(int idShow, const CStdString& strFilenameAndPath)
959 {
960   try
961   {
962     if (NULL == m_pDB.get()) return -1;
963     if (NULL == m_pDS.get()) return -1;
964
965     int idFile = AddFile(strFilenameAndPath);
966     if (idFile < 0)
967       return -1;
968
969     CStdString strSQL=PrepareSQL("insert into episode (idEpisode, idFile) values (NULL, %i)", idFile);
970     m_pDS->exec(strSQL.c_str());
971     int idEpisode = (int)m_pDS->lastinsertid();
972
973     strSQL=PrepareSQL("insert into tvshowlinkepisode (idShow,idEpisode) values (%i,%i)",idShow,idEpisode);
974     m_pDS->exec(strSQL.c_str());
975
976 //    CommitTransaction();
977
978     return idEpisode;
979   }
980   catch (...)
981   {
982     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
983   }
984   return -1;
985 }
986
987 int CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
988 {
989   try
990   {
991     if (NULL == m_pDB.get()) return -1;
992     if (NULL == m_pDS.get()) return -1;
993
994     int idMVideo = GetMusicVideoId(strFilenameAndPath);
995     if (idMVideo < 0)
996     {
997       int idFile = AddFile(strFilenameAndPath);
998       if (idFile < 0)
999         return -1;
1000       CStdString strSQL=PrepareSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %i)", idFile);
1001       m_pDS->exec(strSQL.c_str());
1002       idMVideo = (int)m_pDS->lastinsertid();
1003     }
1004
1005     return idMVideo;
1006   }
1007   catch (...)
1008   {
1009     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1010   }
1011   return -1;
1012 }
1013
1014 //********************************************************************************************************************************
1015 int CVideoDatabase::AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value)
1016 {
1017   try
1018   {
1019     if (NULL == m_pDB.get()) return -1;
1020     if (NULL == m_pDS.get()) return -1;
1021
1022     CStdString strSQL = PrepareSQL("select %s from %s where %s like '%s'", firstField.c_str(), table.c_str(), secondField.c_str(), value.c_str());
1023     m_pDS->query(strSQL.c_str());
1024     if (m_pDS->num_rows() == 0)
1025     {
1026       m_pDS->close();
1027       // doesnt exists, add it
1028       strSQL = PrepareSQL("insert into %s (%s, %s) values( NULL, '%s')", table.c_str(), firstField.c_str(), secondField.c_str(), value.c_str());
1029       m_pDS->exec(strSQL.c_str());
1030       int id = (int)m_pDS->lastinsertid();
1031       return id;
1032     }
1033     else
1034     {
1035       int id = m_pDS->fv(firstField).get_asInt();
1036       m_pDS->close();
1037       return id;
1038     }
1039   }
1040   catch (...)
1041   {
1042     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, value.c_str() );
1043   }
1044
1045   return -1;
1046 }
1047
1048 int CVideoDatabase::AddSet(const CStdString& strSet)
1049 {
1050   return AddToTable("sets", "idSet", "strSet", strSet);
1051 }
1052
1053 int CVideoDatabase::AddGenre(const CStdString& strGenre)
1054 {
1055   return AddToTable("genre", "idGenre", "strGenre", strGenre);
1056 }
1057
1058 int CVideoDatabase::AddStudio(const CStdString& strStudio)
1059 {
1060   return AddToTable("studio", "idStudio", "strStudio", strStudio);
1061 }
1062
1063 //********************************************************************************************************************************
1064 int CVideoDatabase::AddCountry(const CStdString& strCountry)
1065 {
1066   return AddToTable("country", "idCountry", "strCountry", strCountry);
1067 }
1068
1069 int CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& strThumb)
1070 {
1071   try
1072   {
1073     if (NULL == m_pDB.get()) return -1;
1074     if (NULL == m_pDS.get()) return -1;
1075     CStdString strSQL=PrepareSQL("select idActor from actors where strActor like '%s'", strActor.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 actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),strThumb.c_str());
1082       m_pDS->exec(strSQL.c_str());
1083       int idActor = (int)m_pDS->lastinsertid();
1084       return idActor;
1085     }
1086     else
1087     {
1088       const field_value value = m_pDS->fv("idActor");
1089       int idActor = value.get_asInt() ;
1090       // update the thumb url's
1091       if (!strThumb.IsEmpty())
1092         strSQL=PrepareSQL("update actors set strThumb='%s' where idActor=%i",strThumb.c_str(),idActor);
1093       m_pDS->close();
1094       return idActor;
1095     }
1096
1097   }
1098   catch (...)
1099   {
1100     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
1101   }
1102   return -1;
1103 }
1104
1105
1106
1107 void CVideoDatabase::AddLinkToActor(const char *table, int actorID, const char *secondField, int secondID, const CStdString &role)
1108 {
1109   try
1110   {
1111     if (NULL == m_pDB.get()) return ;
1112     if (NULL == m_pDS.get()) return ;
1113
1114     CStdString strSQL=PrepareSQL("select * from %s where idActor=%i and %s=%i", table, actorID, secondField, secondID);
1115     m_pDS->query(strSQL.c_str());
1116     if (m_pDS->num_rows() == 0)
1117     {
1118       // doesnt exists, add it
1119       strSQL=PrepareSQL("insert into %s (idActor, %s, strRole) values(%i,%i,'%s')", table, secondField, actorID, secondID, role.c_str());
1120       m_pDS->exec(strSQL.c_str());
1121     }
1122     m_pDS->close();
1123   }
1124   catch (...)
1125   {
1126     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1127   }
1128 }
1129
1130 void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID)
1131 {
1132   try
1133   {
1134     if (NULL == m_pDB.get()) return ;
1135     if (NULL == m_pDS.get()) return ;
1136
1137     CStdString strSQL=PrepareSQL("select * from %s where %s=%i and %s=%i", table, firstField, firstID, secondField, secondID);
1138     m_pDS->query(strSQL.c_str());
1139     if (m_pDS->num_rows() == 0)
1140     {
1141       // doesnt exists, add it
1142       strSQL=PrepareSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
1143       m_pDS->exec(strSQL.c_str());
1144     }
1145     m_pDS->close();
1146   }
1147   catch (...)
1148   {
1149     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1150   }
1151 }
1152
1153 //****Sets****
1154 void CVideoDatabase::AddSetToMovie(int idMovie, int idSet)
1155 {
1156   AddToLinkTable("setlinkmovie", "idSet", idSet, "idMovie", idMovie);
1157 }
1158
1159 //****Actors****
1160 void CVideoDatabase::AddActorToMovie(int idMovie, int idActor, const CStdString& strRole)
1161 {
1162   AddLinkToActor("actorlinkmovie", idActor, "idMovie", idMovie, strRole);
1163 }
1164
1165 void CVideoDatabase::AddActorToTvShow(int idTvShow, int idActor, const CStdString& strRole)
1166 {
1167   AddLinkToActor("actorlinktvshow", idActor, "idShow", idTvShow, strRole);
1168 }
1169
1170 void CVideoDatabase::AddActorToEpisode(int idEpisode, int idActor, const CStdString& strRole)
1171 {
1172   AddLinkToActor("actorlinkepisode", idActor, "idEpisode", idEpisode, strRole);
1173 }
1174
1175 void CVideoDatabase::AddArtistToMusicVideo(int idMVideo, int idArtist)
1176 {
1177   AddToLinkTable("artistlinkmusicvideo", "idArtist", idArtist, "idMVideo", idMVideo);
1178 }
1179
1180 //****Directors + Writers****
1181 void CVideoDatabase::AddDirectorToMovie(int idMovie, int idDirector)
1182 {
1183   AddToLinkTable("directorlinkmovie", "idDirector", idDirector, "idMovie", idMovie);
1184 }
1185
1186 void CVideoDatabase::AddDirectorToTvShow(int idTvShow, int idDirector)
1187 {
1188   AddToLinkTable("directorlinktvshow", "idDirector", idDirector, "idShow", idTvShow);
1189 }
1190
1191 void CVideoDatabase::AddWriterToEpisode(int idEpisode, int idWriter)
1192 {
1193   AddToLinkTable("writerlinkepisode", "idWriter", idWriter, "idEpisode", idEpisode);
1194 }
1195
1196 void CVideoDatabase::AddWriterToMovie(int idMovie, int idWriter)
1197 {
1198   AddToLinkTable("writerlinkmovie", "idWriter", idWriter, "idMovie", idMovie);
1199 }
1200
1201 void CVideoDatabase::AddDirectorToEpisode(int idEpisode, int idDirector)
1202 {
1203   AddToLinkTable("directorlinkepisode", "idDirector", idDirector, "idEpisode", idEpisode);
1204 }
1205
1206 void CVideoDatabase::AddDirectorToMusicVideo(int idMVideo, int idDirector)
1207 {
1208   AddToLinkTable("directorlinkmusicvideo", "idDirector", idDirector, "idMVideo", idMVideo);
1209 }
1210
1211 //****Studios****
1212 void CVideoDatabase::AddStudioToMovie(int idMovie, int idStudio)
1213 {
1214   AddToLinkTable("studiolinkmovie", "idStudio", idStudio, "idMovie", idMovie);
1215 }
1216
1217 void CVideoDatabase::AddStudioToTvShow(int idTvShow, int idStudio)
1218 {
1219   AddToLinkTable("studiolinktvshow", "idStudio", idStudio, "idShow", idTvShow);
1220 }
1221
1222 void CVideoDatabase::AddStudioToMusicVideo(int idMVideo, int idStudio)
1223 {
1224   AddToLinkTable("studiolinkmusicvideo", "idStudio", idStudio, "idMVideo", idMVideo);
1225 }
1226
1227 //****Genres****
1228 void CVideoDatabase::AddGenreToMovie(int idMovie, int idGenre)
1229 {
1230   AddToLinkTable("genrelinkmovie", "idGenre", idGenre, "idMovie", idMovie);
1231 }
1232
1233 void CVideoDatabase::AddGenreToTvShow(int idTvShow, int idGenre)
1234 {
1235   AddToLinkTable("genrelinktvshow", "idGenre", idGenre, "idShow", idTvShow);
1236 }
1237
1238 void CVideoDatabase::AddGenreToMusicVideo(int idMVideo, int idGenre)
1239 {
1240   AddToLinkTable("genrelinkmusicvideo", "idGenre", idGenre, "idMVideo", idMVideo);
1241 }
1242
1243 //****Country****
1244 void CVideoDatabase::AddCountryToMovie(int idMovie, int idCountry)
1245 {
1246   AddToLinkTable("countrylinkmovie", "idCountry", idCountry, "idMovie", idMovie);
1247 }
1248
1249 //********************************************************************************************************************************
1250 bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
1251 {
1252   try
1253   {
1254     if (NULL == m_pDB.get()) return false;
1255     if (NULL == m_pDS.get()) return false;
1256     int idMovie = GetMovieId(strFilenameAndPath);
1257     return (idMovie > 0); // index of zero is also invalid
1258
1259     // work in progress
1260     if (idMovie > 0)
1261     {
1262       // get title.  if no title, the id was "deleted" for in-place update
1263       CVideoInfoTag details;
1264       GetMovieInfo(strFilenameAndPath, details, idMovie);
1265       if (!details.m_strTitle.IsEmpty()) return true;
1266     }
1267     return false;
1268   }
1269   catch (...)
1270   {
1271     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1272   }
1273   return false;
1274 }
1275
1276 bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
1277 {
1278   try
1279   {
1280     if (NULL == m_pDB.get()) return false;
1281     if (NULL == m_pDS.get()) return false;
1282     int idTvShow = GetTvShowId(strPath);
1283     return (idTvShow > 0); // index of zero is also invalid
1284
1285     // work in progress
1286     if (idTvShow > 0)
1287     {
1288       // get title. if no title, the id was "deleted" for in-place update
1289       CVideoInfoTag details;
1290       GetTvShowInfo(strPath, details, idTvShow);
1291       if (!details.m_strTitle.IsEmpty()) return true;
1292     }
1293     return false;
1294   }
1295   catch (...)
1296   {
1297     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1298   }
1299   return false;
1300 }
1301
1302 bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
1303 {
1304   try
1305   {
1306     if (NULL == m_pDB.get()) return false;
1307     if (NULL == m_pDS.get()) return false;
1308     int idEpisode = GetEpisodeId(strFilenameAndPath);
1309     return (idEpisode > 0); // index of zero is also invalid
1310
1311     // work in progress
1312     if (idEpisode > 0)
1313     {
1314       // get title.  if no title, the id was "deleted" for in-place update
1315       CVideoInfoTag details;
1316       GetEpisodeInfo(strFilenameAndPath, details, idEpisode);
1317       if (!details.m_strTitle.IsEmpty()) return true;
1318     }
1319     return false;
1320   }
1321   catch (...)
1322   {
1323     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1324   }
1325   return false;
1326 }
1327
1328 bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
1329 {
1330   try
1331   {
1332     if (NULL == m_pDB.get()) return false;
1333     if (NULL == m_pDS.get()) return false;
1334     int idMVideo = GetMusicVideoId(strFilenameAndPath);
1335     return (idMVideo > 0); // index of zero is also invalid
1336
1337     // work in progress
1338     if (idMVideo > 0)
1339     {
1340       // get title.  if no title, the id was "deleted" for in-place update
1341       CVideoInfoTag details;
1342       GetMusicVideoInfo(strFilenameAndPath, details, idMVideo);
1343       if (!details.m_strTitle.IsEmpty()) return true;
1344     }
1345     return false;
1346   }
1347   catch (...)
1348   {
1349     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1350   }
1351   return false;
1352 }
1353
1354 void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath)
1355 {// TODO: merge into DeleteTvShow
1356   try
1357   {
1358     if (NULL == m_pDB.get()) return ;
1359     if (NULL == m_pDS.get()) return ;
1360
1361     int idTvShow = GetTvShowId(strPath);
1362     if ( idTvShow < 0) return ;
1363
1364     CFileItemList items;
1365     CStdString strPath2;
1366     strPath2.Format("videodb://2/2/%i/",idTvShow);
1367     GetSeasonsNav(strPath2,items,-1,-1,-1,-1,idTvShow);
1368     for( int i=0;i<items.Size();++i )
1369       CTextureCache::Get().ClearCachedImage(items[i]->GetCachedSeasonThumb(), true);
1370     DeleteThumbForItem(strPath,true);
1371
1372     CStdString strSQL;
1373     strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
1374     m_pDS->exec(strSQL.c_str());
1375
1376     strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
1377     m_pDS->exec(strSQL.c_str());
1378
1379     strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
1380     m_pDS->exec(strSQL.c_str());
1381
1382     strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
1383     m_pDS->exec(strSQL.c_str());
1384
1385     // remove all info other than the id
1386     // we do this due to the way we have the link between the file + movie tables.
1387
1388     strSQL = "update tvshow set ";
1389     for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1390     {
1391       CStdString column;
1392       column.Format("c%02d=NULL,", iType);
1393       strSQL += column;
1394     }
1395     strSQL = strSQL.Mid(0, strSQL.size() - 1) + PrepareSQL(" where idShow=%i", idTvShow);
1396     m_pDS->exec(strSQL.c_str());
1397   }
1398   catch (...)
1399   {
1400     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1401   }
1402 }
1403
1404 //********************************************************************************************************************************
1405 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
1406 {
1407   CStdString where = PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie=movieview.idMovie "
1408                                 "join actors on actors.idActor=actorlinkmovie.idActor "
1409                                 "where actors.strActor='%s'", strActor.c_str());
1410   GetMoviesByWhere("videodb://1/2/", where, "", items);
1411 }
1412
1413 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
1414 {
1415   CStdString where = PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow=tvshow.idShow "
1416                                "join actors on actors.idActor=actorlinktvshow.idActor "
1417                                "where actors.strActor='%s'", strActor.c_str());
1418   GetTvShowsByWhere("videodb://2/2/", where, items);
1419 }
1420
1421 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
1422 {
1423   CStdString where = PrepareSQL("join actorlinkepisode on actorlinkepisode.idEpisode=episodeview.idEpisode "
1424                                "join actors on actors.idActor=actorlinkepisode.idActor "
1425                                "where actors.strActor='%s'", strActor.c_str());
1426   GetEpisodesByWhere("videodb://2/2/", where, items);
1427 }
1428
1429 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1430 {
1431   try
1432   {
1433     items.Clear();
1434     if (NULL == m_pDB.get()) return ;
1435     if (NULL == m_pDS.get()) return ;
1436
1437     CStdString strSQL;
1438     if (strArtist.IsEmpty())  // TODO: SMARTPLAYLISTS what is this here for???
1439       strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1440     else
1441       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());
1442     m_pDS->query( strSQL.c_str() );
1443
1444     while (!m_pDS->eof())
1445     {
1446       CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1447       CFileItemPtr pItem(new CFileItem(tag));
1448       pItem->SetLabel(tag.m_strArtist);
1449       items.Add(pItem);
1450       m_pDS->next();
1451     }
1452     m_pDS->close();
1453   }
1454   catch (...)
1455   {
1456     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1457   }
1458 }
1459
1460 //********************************************************************************************************************************
1461 void CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
1462 {
1463   try
1464   {
1465     // TODO: Optimize this - no need for all the queries!
1466     if (idMovie < 0)
1467       idMovie = GetMovieId(strFilenameAndPath);
1468     if (idMovie < 0) return ;
1469
1470     CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
1471     if (!m_pDS->query(sql.c_str()))
1472       return;
1473     details = GetDetailsForMovie(m_pDS, true);
1474   }
1475   catch (...)
1476   {
1477     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1478   }
1479 }
1480
1481 //********************************************************************************************************************************
1482 void CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
1483 {
1484   try
1485   {
1486     if (idTvShow < 0)
1487       idTvShow = GetTvShowId(strPath);
1488     if (idTvShow < 0) return ;
1489
1490     CStdString sql = PrepareSQL(VIDEO_DATABASE_VIEW_TVSHOW " WHERE tvshow.idShow=%i", idTvShow);
1491     if (!m_pDS->query(sql.c_str()))
1492       return;
1493     details = GetDetailsForTvShow(m_pDS, true);
1494   }
1495   catch (...)
1496   {
1497     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1498   }
1499 }
1500
1501 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
1502 {
1503   try
1504   {
1505     // TODO: Optimize this - no need for all the queries!
1506     if (idEpisode < 0)
1507       idEpisode = GetEpisodeId(strFilenameAndPath);
1508     if (idEpisode < 0) return false;
1509
1510     CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
1511     if (!m_pDS->query(sql.c_str()))
1512       return false;
1513     details = GetDetailsForEpisode(m_pDS, true);
1514     return true;
1515   }
1516   catch (...)
1517   {
1518     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1519   }
1520   return false;
1521 }
1522
1523 void CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
1524 {
1525   try
1526   {
1527     // TODO: Optimize this - no need for all the queries!
1528     if (idMVideo < 0)
1529       idMVideo = GetMusicVideoId(strFilenameAndPath);
1530     if (idMVideo < 0) return ;
1531
1532     CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
1533     if (!m_pDS->query(sql.c_str()))
1534       return;
1535     details = GetDetailsForMusicVideo(m_pDS);
1536   }
1537   catch (...)
1538   {
1539     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1540   }
1541 }
1542
1543 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1544 {
1545   // add all directors
1546   if (!details.m_strDirector.IsEmpty())
1547   {
1548     CStdStringArray directors;
1549     StringUtils::SplitString(details.m_strDirector, g_advancedSettings.m_videoItemSeparator, directors);
1550     for (unsigned int i = 0; i < directors.size(); i++)
1551     {
1552       CStdString strDirector(directors[i]);
1553       strDirector.Trim();
1554       int idDirector = AddActor(strDirector,"");
1555       vecDirectors.push_back(idDirector);
1556     }
1557   }
1558
1559   // add all genres
1560   if (!details.m_strGenre.IsEmpty())
1561   {
1562     CStdStringArray genres;
1563     StringUtils::SplitString(details.m_strGenre, g_advancedSettings.m_videoItemSeparator, genres);
1564     for (unsigned int i = 0; i < genres.size(); i++)
1565     {
1566       CStdString strGenre(genres[i]);
1567       strGenre.Trim();
1568       int idGenre = AddGenre(strGenre);
1569       vecGenres.push_back(idGenre);
1570     }
1571   }
1572   // add all studios
1573   if (!details.m_strStudio.IsEmpty())
1574   {
1575     CStdStringArray studios;
1576     StringUtils::SplitString(details.m_strStudio, g_advancedSettings.m_videoItemSeparator, studios);
1577     for (unsigned int i = 0; i < studios.size(); i++)
1578     {
1579       CStdString strStudio(studios[i]);
1580       strStudio.Trim();
1581       int idStudio = AddStudio(strStudio);
1582       vecStudios.push_back(idStudio);
1583     }
1584   }
1585 }
1586
1587 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1588 {
1589   CStdString sql;
1590   for (int i = min + 1; i < max; ++i)
1591   {
1592     switch (offsets[i].type)
1593     {
1594     case VIDEODB_TYPE_STRING:
1595       sql += PrepareSQL("c%02d='%s',", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str());
1596       break;
1597     case VIDEODB_TYPE_INT:
1598       sql += PrepareSQL("c%02d='%i',", i, *(int*)(((char*)&details)+offsets[i].offset));
1599       break;
1600     case VIDEODB_TYPE_COUNT:
1601       {
1602         int value = *(int*)(((char*)&details)+offsets[i].offset);
1603         if (value)
1604           sql += PrepareSQL("c%02d=%i,", i, value);
1605         else
1606           sql += PrepareSQL("c%02d=NULL,", i);
1607       }
1608       break;
1609     case VIDEODB_TYPE_BOOL:
1610       sql += PrepareSQL("c%02d='%s',", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false");
1611       break;
1612     case VIDEODB_TYPE_FLOAT:
1613       sql += PrepareSQL("c%02d='%f',", i, *(float*)(((char*)&details)+offsets[i].offset));
1614       break;
1615     }
1616   }
1617   sql.TrimRight(',');
1618   return sql;
1619 }
1620
1621 //********************************************************************************************************************************
1622 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details)
1623 {
1624   try
1625   {
1626     CVideoInfoTag info = details;
1627
1628     int idMovie = GetMovieId(strFilenameAndPath);
1629     if (idMovie > -1)
1630       DeleteMovie(strFilenameAndPath, true); // true to keep the table entry
1631
1632     BeginTransaction();
1633
1634     idMovie = AddMovie(strFilenameAndPath);
1635     if (idMovie < 0)
1636     {
1637       CommitTransaction();
1638       return idMovie;
1639     }
1640
1641     vector<int> vecDirectors;
1642     vector<int> vecGenres;
1643     vector<int> vecStudios;
1644     AddGenreAndDirectorsAndStudios(info,vecDirectors,vecGenres,vecStudios);
1645
1646     for (unsigned int i = 0; i < vecGenres.size(); ++i)
1647       AddGenreToMovie(idMovie, vecGenres[i]);
1648
1649     for (unsigned int i = 0; i < vecDirectors.size(); ++i)
1650       AddDirectorToMovie(idMovie, vecDirectors[i]);
1651
1652     for (unsigned int i = 0; i < vecStudios.size(); ++i)
1653       AddStudioToMovie(idMovie, vecStudios[i]);
1654
1655     // add writers...
1656     if (!info.m_strWritingCredits.IsEmpty())
1657     {
1658       CStdStringArray writers;
1659       StringUtils::SplitString(info.m_strWritingCredits, g_advancedSettings.m_videoItemSeparator, writers);
1660       for (unsigned int i = 0; i < writers.size(); i++)
1661       {
1662         CStdString writer(writers[i]);
1663         writer.Trim();
1664         int idWriter = AddActor(writer,"");
1665         AddWriterToMovie(idMovie, idWriter );
1666       }
1667     }
1668
1669     // add cast...
1670     for (CVideoInfoTag::iCast it = info.m_cast.begin(); it != info.m_cast.end(); ++it)
1671     {
1672       int idActor = AddActor(it->strName,it->thumbUrl.m_xml);
1673       AddActorToMovie(idMovie, idActor, it->strRole);
1674     }
1675
1676     // add sets...
1677     if (!info.m_strSet.IsEmpty())
1678     {
1679       CStdStringArray sets;
1680       StringUtils::SplitString(info.m_strSet, g_advancedSettings.m_videoItemSeparator, sets);
1681       for (unsigned int i = 0; i < sets.size(); i++)
1682       {
1683         CStdString set(sets[i]);
1684         set.Trim();
1685         int idSet = AddSet(set);
1686         AddSetToMovie(idMovie, idSet);
1687       }
1688     }
1689
1690     // add countries...
1691     if (!info.m_strCountry.IsEmpty())
1692     {
1693       CStdStringArray countries;
1694       StringUtils::SplitString(info.m_strCountry, g_advancedSettings.m_videoItemSeparator, countries);
1695       for (unsigned int i = 0; i < countries.size(); i++)
1696       {
1697         CStdString country(countries[i]);
1698         country.Trim();
1699         int idCountry = AddCountry(country);
1700         AddCountryToMovie(idMovie, idCountry);
1701       }
1702     }
1703
1704     if (details.HasStreamDetails())
1705       SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
1706
1707     // update our movie table (we know it was added already above)
1708     // and insert the new row
1709     CStdString sql = "update movie set " + GetValueString(info, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
1710     sql += PrepareSQL(" where idMovie=%i", idMovie);
1711     m_pDS->exec(sql.c_str());
1712     CommitTransaction();
1713
1714     AnnounceUpdate("movie", idMovie);
1715
1716     return idMovie;
1717   }
1718   catch (...)
1719   {
1720     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1721   }
1722   return -1;
1723 }
1724
1725 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details)
1726 {
1727   try
1728   {
1729     if (!m_pDB.get() || !m_pDS.get())
1730     {
1731       CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
1732       return -1;
1733     }
1734
1735     BeginTransaction();
1736
1737     int idTvShow = GetTvShowId(strPath);
1738     if (idTvShow < 0)
1739       idTvShow = AddTvShow(strPath);
1740
1741     vector<int> vecDirectors;
1742     vector<int> vecGenres;
1743     vector<int> vecStudios;
1744     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1745
1746     // add cast...
1747     for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
1748     {
1749       int idActor = AddActor(it->strName,it->thumbUrl.m_xml);
1750       AddActorToTvShow(idTvShow, idActor, it->strRole);
1751     }
1752
1753     unsigned int i;
1754     for (i = 0; i < vecGenres.size(); ++i)
1755     {
1756       AddGenreToTvShow(idTvShow, vecGenres[i]);
1757     }
1758
1759     for (i = 0; i < vecDirectors.size(); ++i)
1760     {
1761       AddDirectorToTvShow(idTvShow, vecDirectors[i]);
1762     }
1763
1764     for (i = 0; i < vecStudios.size(); ++i)
1765     {
1766       AddStudioToTvShow(idTvShow, vecStudios[i]);
1767     }
1768
1769     // and insert the new row
1770     CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
1771     sql += PrepareSQL("where idShow=%i", idTvShow);
1772     m_pDS->exec(sql.c_str());
1773     CommitTransaction();
1774
1775     AnnounceUpdate("tvshow", idTvShow);
1776
1777     return idTvShow;
1778   }
1779   catch (...)
1780   {
1781     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1782   }
1783
1784   return -1;
1785 }
1786
1787 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, int idShow, int idEpisode)
1788 {
1789   try
1790   {
1791     BeginTransaction();
1792     if (idEpisode == -1)
1793     {
1794       idEpisode = GetEpisodeId(strFilenameAndPath);
1795       if (idEpisode > 0)
1796         DeleteEpisode(strFilenameAndPath,idEpisode);
1797
1798       idEpisode = AddEpisode(idShow,strFilenameAndPath);
1799       if (idEpisode < 0)
1800       {
1801         CommitTransaction();
1802         return -1;
1803       }
1804     }
1805
1806     vector<int> vecDirectors;
1807     vector<int> vecGenres;
1808     vector<int> vecStudios;
1809     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1810
1811     // add cast...
1812     for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
1813     {
1814       int idActor = AddActor(it->strName,it->thumbUrl.m_xml);
1815       AddActorToEpisode(idEpisode, idActor, it->strRole);
1816     }
1817
1818     // add writers...
1819     if (!details.m_strWritingCredits.IsEmpty())
1820     {
1821       CStdStringArray writers;
1822       StringUtils::SplitString(details.m_strWritingCredits, g_advancedSettings.m_videoItemSeparator, writers);
1823       for (unsigned int i = 0; i < writers.size(); i++)
1824       {
1825         CStdString writer(writers[i]);
1826         writer.Trim();
1827         int idWriter = AddActor(writer,"");
1828         AddWriterToEpisode(idEpisode, idWriter );
1829       }
1830     }
1831
1832     for (unsigned int i = 0; i < vecDirectors.size(); ++i)
1833     {
1834       AddDirectorToEpisode(idEpisode, vecDirectors[i]);
1835     }
1836
1837     if (details.HasStreamDetails())
1838     {
1839       if (details.m_iFileId != -1)
1840         SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
1841       else
1842         SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
1843     }
1844
1845     // and insert the new row
1846     CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
1847     sql += PrepareSQL("where idEpisode=%i", idEpisode);
1848     m_pDS->exec(sql.c_str());
1849     CommitTransaction();
1850
1851     AnnounceUpdate("episode", idEpisode);
1852
1853     return idEpisode;
1854   }
1855   catch (...)
1856   {
1857     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1858   }
1859   return -1;
1860 }
1861
1862 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details)
1863 {
1864   try
1865   {
1866     BeginTransaction();
1867
1868     int idMVideo = GetMusicVideoId(strFilenameAndPath);
1869     if (idMVideo > -1)
1870     {
1871       DeleteMusicVideo(strFilenameAndPath);
1872     }
1873     idMVideo = AddMusicVideo(strFilenameAndPath);
1874     if (idMVideo < 0)
1875     {
1876       CommitTransaction();
1877       return -1;
1878     }
1879
1880     vector<int> vecDirectors;
1881     vector<int> vecGenres;
1882     vector<int> vecStudios;
1883     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1884
1885     // add artists...
1886     if (!details.m_strArtist.IsEmpty())
1887     {
1888       CStdStringArray vecArtists;
1889       StringUtils::SplitString(details.m_strArtist, g_advancedSettings.m_videoItemSeparator, vecArtists);
1890       for (unsigned int i = 0; i < vecArtists.size(); i++)
1891       {
1892         CStdString artist = vecArtists[i];
1893         artist.Trim();
1894         int idArtist = AddActor(artist,"");
1895         AddArtistToMusicVideo(idMVideo, idArtist);
1896       }
1897     }
1898
1899     unsigned int i;
1900     for (i = 0; i < vecGenres.size(); ++i)
1901     {
1902       AddGenreToMusicVideo(idMVideo, vecGenres[i]);
1903     }
1904
1905     for (i = 0; i < vecDirectors.size(); ++i)
1906     {
1907       AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
1908     }
1909
1910     for (i = 0; i < vecStudios.size(); ++i)
1911     {
1912       AddStudioToMusicVideo(idMVideo, vecStudios[i]);
1913     }
1914
1915     if (details.HasStreamDetails())
1916       SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
1917
1918     // update our movie table (we know it was added already above)
1919     // and insert the new row
1920     CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
1921     sql += PrepareSQL(" where idMVideo=%i", idMVideo);
1922     m_pDS->exec(sql.c_str());
1923     CommitTransaction();
1924
1925     AnnounceUpdate("musicvideo", idMVideo);
1926
1927     return idMVideo;
1928   }
1929   catch (...)
1930   {
1931     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1932   }
1933   return -1;
1934 }
1935
1936 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
1937 {
1938   // AddFile checks to make sure the file isn't already in the DB first
1939   int idFile = AddFile(strFileNameAndPath);
1940   if (idFile < 0)
1941     return;
1942   SetStreamDetailsForFileId(details, idFile);
1943 }
1944
1945 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
1946 {
1947   if (idFile < 0)
1948     return;
1949
1950   try
1951   {
1952     BeginTransaction();
1953     m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
1954
1955     for (int i=1; i<=details.GetVideoStreamCount(); i++)
1956     {
1957       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
1958         "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration) "
1959         "VALUES (%i,%i,'%s',%f,%i,%i,%i)",
1960         idFile, (int)CStreamDetail::VIDEO,
1961         details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
1962         details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i)));
1963     }
1964     for (int i=1; i<=details.GetAudioStreamCount(); i++)
1965     {
1966       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
1967         "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
1968         "VALUES (%i,%i,'%s',%i,'%s')",
1969         idFile, (int)CStreamDetail::AUDIO,
1970         details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
1971         details.GetAudioLanguage(i).c_str()));
1972     }
1973     for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
1974     {
1975       m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
1976         "(idFile, iStreamType, strSubtitleLanguage) "
1977         "VALUES (%i,%i,'%s')",
1978         idFile, (int)CStreamDetail::SUBTITLE,
1979         details.GetSubtitleLanguage(i).c_str()));
1980     }
1981
1982     CommitTransaction();
1983   }
1984   catch (...)
1985   {
1986     RollbackTransaction();
1987     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
1988   }
1989 }
1990
1991 //********************************************************************************************************************************
1992 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
1993 {
1994   try
1995   {
1996     if (NULL == m_pDB.get()) return ;
1997     if (NULL == m_pDS.get()) return ;
1998
1999     if (idMovie < 0) return ;
2000
2001     CStdString strSQL;
2002     if (iType == VIDEODB_CONTENT_MOVIES)
2003       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 );
2004     if (iType == VIDEODB_CONTENT_EPISODES)
2005       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 );
2006     if (iType == VIDEODB_CONTENT_TVSHOWS)
2007       strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2008     if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2009       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 );
2010
2011     m_pDS->query( strSQL.c_str() );
2012     if (!m_pDS->eof())
2013     {
2014       if (iType != VIDEODB_CONTENT_TVSHOWS)
2015       {
2016         CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2017         ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2018       }
2019       else
2020         filePath = m_pDS->fv("path.strPath").get_asString();
2021     }
2022     m_pDS->close();
2023   }
2024   catch (...)
2025   {
2026     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2027   }
2028 }
2029
2030 //********************************************************************************************************************************
2031 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend)
2032 {
2033   try
2034   {
2035     int idFile = GetFileId(strFilenameAndPath);
2036     if (idFile < 0) return ;
2037     if (!bAppend)
2038       bookmarks.erase(bookmarks.begin(), bookmarks.end());
2039     if (NULL == m_pDB.get()) return ;
2040     if (NULL == m_pDS.get()) return ;
2041
2042     CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2043     m_pDS->query( strSQL.c_str() );
2044     while (!m_pDS->eof())
2045     {
2046       CBookmark bookmark;
2047       bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2048       bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2049       bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2050       bookmark.playerState = m_pDS->fv("playerState").get_asString();
2051       bookmark.player = m_pDS->fv("player").get_asString();
2052       bookmark.type = type;
2053       if (type == CBookmark::EPISODE)
2054       {
2055         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);
2056         m_pDS2->query(strSQL2.c_str());
2057         bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2058         bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2059         m_pDS2->close();
2060       }
2061       bookmarks.push_back(bookmark);
2062       m_pDS->next();
2063     }
2064     //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2065     m_pDS->close();
2066   }
2067   catch (...)
2068   {
2069     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2070   }
2071 }
2072
2073 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2074 {
2075   VECBOOKMARKS bookmarks;
2076   GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2077   if (bookmarks.size() > 0)
2078   {
2079     bookmark = bookmarks[0];
2080     return true;
2081   }
2082   return false;
2083 }
2084
2085 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2086 {
2087   if (!m_pDB.get() || !m_pDS.get())
2088     return;
2089
2090   int fileID = GetFileId(strFilenameAndPath);
2091   if (fileID < -1)
2092     return;
2093
2094   try
2095   {
2096     CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2097     m_pDS->exec(sql.c_str());
2098   }
2099   catch(...)
2100   {
2101     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2102   }
2103 }
2104
2105 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2106 {
2107   try
2108   {
2109     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);
2110     m_pDS->query(strSQL.c_str());
2111     while (!m_pDS->eof())
2112     {
2113       episodes.push_back(GetDetailsForEpisode(m_pDS));
2114       m_pDS->next();
2115     }
2116     m_pDS->close();
2117   }
2118   catch (...)
2119   {
2120     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2121   }
2122 }
2123
2124 //********************************************************************************************************************************
2125 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2126 {
2127   try
2128   {
2129     int idFile = AddFile(strFilenameAndPath);
2130     if (idFile < 0)
2131       return;
2132     if (NULL == m_pDB.get()) return ;
2133     if (NULL == m_pDS.get()) return ;
2134
2135     CStdString strSQL;
2136     int idBookmark=-1;
2137     if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2138     {
2139       strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2140     }
2141     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
2142     {
2143       /* get a bookmark within the same time as previous */
2144       double mintime = bookmark.timeInSeconds - 0.5f;
2145       double maxtime = bookmark.timeInSeconds + 0.5f;
2146       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());
2147     }
2148
2149     if (type != CBookmark::EPISODE)
2150     {
2151       // get current id
2152       m_pDS->query( strSQL.c_str() );
2153       if (m_pDS->num_rows() != 0)
2154         idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2155       m_pDS->close();
2156     }
2157     // update or insert depending if it existed before
2158     if (idBookmark >= 0 )
2159       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);
2160     else
2161       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);
2162
2163     m_pDS->exec(strSQL.c_str());
2164   }
2165   catch (...)
2166   {
2167     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2168   }
2169 }
2170
2171 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2172 {
2173   try
2174   {
2175     int idFile = GetFileId(strFilenameAndPath);
2176     if (idFile < 0) return ;
2177     if (NULL == m_pDB.get()) return ;
2178     if (NULL == m_pDS.get()) return ;
2179
2180     /* a litle bit uggly, we clear first bookmark that is within one second of given */
2181     /* should be no problem since we never add bookmarks that are closer than that   */
2182     double mintime = bookmark.timeInSeconds - 0.5f;
2183     double maxtime = bookmark.timeInSeconds + 0.5f;
2184     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);
2185
2186     m_pDS->query( strSQL.c_str() );
2187     if (m_pDS->num_rows() != 0)
2188     {
2189       int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2190       strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2191       m_pDS->exec(strSQL.c_str());
2192       if (type == CBookmark::EPISODE)
2193       {
2194         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);
2195         m_pDS->exec(strSQL.c_str());
2196       }
2197     }
2198
2199     m_pDS->close();
2200   }
2201   catch (...)
2202   {
2203     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2204   }
2205 }
2206
2207 //********************************************************************************************************************************
2208 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2209 {
2210   try
2211   {
2212     int idFile = GetFileId(strFilenameAndPath);
2213     if (idFile < 0) return ;
2214     if (NULL == m_pDB.get()) return ;
2215     if (NULL == m_pDS.get()) return ;
2216
2217     CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2218     m_pDS->exec(strSQL.c_str());
2219     if (type == CBookmark::EPISODE)
2220     {
2221       strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2222       m_pDS->exec(strSQL.c_str());
2223     }
2224   }
2225   catch (...)
2226   {
2227     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2228   }
2229 }
2230
2231
2232 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2233 {
2234   try
2235   {
2236     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);
2237     m_pDS->query( strSQL.c_str() );
2238     if (!m_pDS->eof())
2239     {
2240       bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2241       bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2242       bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2243       bookmark.playerState = m_pDS->fv("playerState").get_asString();
2244       bookmark.player = m_pDS->fv("player").get_asString();
2245       bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2246     }
2247     else
2248     {
2249       m_pDS->close();
2250       return false;
2251     }
2252     m_pDS->close();
2253   }
2254   catch (...)
2255   {
2256     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2257     return false;
2258   }
2259   return true;
2260 }
2261
2262 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2263 {
2264   try
2265   {
2266     int idFile = GetFileId(tag.m_strFileNameAndPath);
2267     // delete the current episode for the selected episode number
2268     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);
2269     m_pDS->exec(strSQL.c_str());
2270
2271     AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2272     int idBookmark = (int)m_pDS->lastinsertid();
2273     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);
2274     m_pDS->exec(strSQL.c_str());
2275   }
2276   catch (...)
2277   {
2278     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2279   }
2280 }
2281
2282 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2283 {
2284   try
2285   {
2286     CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2287     m_pDS->exec(strSQL.c_str());
2288     strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2289     m_pDS->exec(strSQL.c_str());
2290   }
2291   catch (...)
2292   {
2293     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2294   }
2295 }
2296
2297 //********************************************************************************************************************************
2298 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2299 {
2300   try
2301   {
2302     if (NULL == m_pDB.get()) return ;
2303     if (NULL == m_pDS.get()) return ;
2304     int idMovie = GetMovieId(strFilenameAndPath);
2305     if (idMovie < 0)
2306     {
2307       return ;
2308     }
2309
2310     BeginTransaction();
2311
2312     CStdString strSQL;
2313     strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2314     m_pDS->exec(strSQL.c_str());
2315
2316     strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2317     m_pDS->exec(strSQL.c_str());
2318
2319     strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2320     m_pDS->exec(strSQL.c_str());
2321
2322     strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2323     m_pDS->exec(strSQL.c_str());
2324
2325     strSQL=PrepareSQL("delete from setlinkmovie where idMovie=%i", idMovie);
2326     m_pDS->exec(strSQL.c_str());
2327
2328     strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2329     m_pDS->exec(strSQL.c_str());
2330
2331     if (!bKeepThumb)
2332       DeleteThumbForItem(strFilenameAndPath,false);
2333
2334     DeleteStreamDetails(GetFileId(strFilenameAndPath));
2335
2336     // keep the movie table entry, linking to tv shows, and bookmarks
2337     // so we can update the data in place
2338     // the ancilliary tables are still purged
2339     if (!bKeepId)
2340     {
2341       ClearBookMarksOfFile(strFilenameAndPath);
2342
2343       strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2344       m_pDS->exec(strSQL.c_str());
2345
2346       strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2347       m_pDS->exec(strSQL.c_str());
2348     }
2349     /*
2350     // work in progress
2351     else
2352     {
2353       // clear the title
2354       strSQL=PrepareSQL("update movie set c%02d=NULL where idmovie=%i", VIDEODB_ID_TITLE, idMovie);
2355       m_pDS->exec(strSQL.c_str());
2356     }
2357     */
2358
2359     CStdString strPath, strFileName;
2360     SplitPath(strFilenameAndPath,strPath,strFileName);
2361     InvalidatePathHash(strPath);
2362     CommitTransaction();
2363
2364     if (!bKeepId)
2365       AnnounceRemove("movie", idMovie);
2366   }
2367   catch (...)
2368   {
2369     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2370   }
2371 }
2372
2373 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2374 {
2375   try
2376   {
2377     int idTvShow=-1;
2378     if (NULL == m_pDB.get()) return ;
2379     if (NULL == m_pDS.get()) return ;
2380     idTvShow = GetTvShowId(strPath);
2381     if (idTvShow < 0)
2382     {
2383       return ;
2384     }
2385
2386     BeginTransaction();
2387
2388     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);
2389     m_pDS2->query(strSQL.c_str());
2390     while (!m_pDS2->eof())
2391     {
2392       CStdString strFilenameAndPath;
2393       CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2394       CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2395       ConstructPath(strFilenameAndPath, strPath, strFileName);
2396       DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2397       m_pDS2->next();
2398     }
2399
2400     strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
2401     m_pDS->exec(strSQL.c_str());
2402
2403     strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
2404     m_pDS->exec(strSQL.c_str());
2405
2406     strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
2407     m_pDS->exec(strSQL.c_str());
2408
2409     strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2410     m_pDS->exec(strSQL.c_str());
2411
2412     strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
2413     m_pDS->exec(strSQL.c_str());
2414
2415     if (!bKeepThumb)
2416       DeleteThumbForItem(strPath,true);
2417
2418     // keep tvshow table and movielink table so we can update data in place
2419     if (!bKeepId)
2420     {
2421       strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2422       m_pDS->exec(strSQL.c_str());
2423
2424       strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2425       m_pDS->exec(strSQL.c_str());
2426     }
2427
2428     InvalidatePathHash(strPath);
2429
2430     CommitTransaction();
2431
2432     if (!bKeepId)
2433       AnnounceRemove("tvshow", idTvShow);
2434   }
2435   catch (...)
2436   {
2437     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2438   }
2439 }
2440
2441 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2442 {
2443   try
2444   {
2445     if (NULL == m_pDB.get()) return ;
2446     if (NULL == m_pDS.get()) return ;
2447     if (idEpisode < 0)
2448     {
2449       idEpisode = GetEpisodeId(strFilenameAndPath);
2450       if (idEpisode < 0)
2451       {
2452         return ;
2453       }
2454     }
2455
2456     CStdString strSQL;
2457     strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
2458     m_pDS->exec(strSQL.c_str());
2459
2460     strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
2461     m_pDS->exec(strSQL.c_str());
2462
2463     strSQL=PrepareSQL("select tvshowlinkepisode.idshow from tvshowlinkepisode where idEpisode=%i",idEpisode);
2464     m_pDS->query(strSQL.c_str());
2465
2466     strSQL=PrepareSQL("delete from tvshowlinkepisode where idEpisode=%i", idEpisode);
2467     m_pDS->exec(strSQL.c_str());
2468
2469     if (!bKeepThumb)
2470       DeleteThumbForItem(strFilenameAndPath, false, idEpisode);
2471
2472     DeleteStreamDetails(GetFileId(strFilenameAndPath));
2473
2474     // keep episode table entry and bookmarks so we can update the data in place
2475     // the ancilliary tables are still purged
2476     if (!bKeepId)
2477     {
2478       ClearBookMarksOfFile(strFilenameAndPath);
2479
2480       strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
2481       m_pDS->exec(strSQL.c_str());
2482     }
2483
2484     if (!bKeepId)
2485       AnnounceRemove("episode", idEpisode);
2486   }
2487   catch (...)
2488   {
2489     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2490   }
2491 }
2492
2493 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2494 {
2495   try
2496   {
2497     if (NULL == m_pDB.get()) return ;
2498     if (NULL == m_pDS.get()) return ;
2499     int idMVideo = GetMusicVideoId(strFilenameAndPath);
2500     if (idMVideo < 0)
2501     {
2502       return ;
2503     }
2504
2505     BeginTransaction();
2506
2507     CStdString strSQL;
2508     strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
2509     m_pDS->exec(strSQL.c_str());
2510
2511     strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
2512     m_pDS->exec(strSQL.c_str());
2513
2514     strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
2515     m_pDS->exec(strSQL.c_str());
2516
2517     strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
2518     m_pDS->exec(strSQL.c_str());
2519
2520     if (!bKeepThumb)
2521       DeleteThumbForItem(strFilenameAndPath,false);
2522
2523     DeleteStreamDetails(GetFileId(strFilenameAndPath));
2524
2525     // keep the music video table entry and bookmarks so we can update data in place
2526     // the ancilliary tables are still purged
2527     if (!bKeepId)
2528     {
2529       ClearBookMarksOfFile(strFilenameAndPath);
2530
2531       strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
2532       m_pDS->exec(strSQL.c_str());
2533     }
2534     /*
2535     // work in progress
2536     else
2537     {
2538       // clear the title
2539       strSQL=PrepareSQL("update musicvideo set c%02d=NULL where idmvideo=%i", VIDEODB_ID_MUSICVIDEO_TITLE, idMVideo);
2540       m_pDS->exec(strSQL.c_str());
2541     }
2542     */
2543
2544     CStdString strPath, strFileName;
2545     SplitPath(strFilenameAndPath,strPath,strFileName);
2546     InvalidatePathHash(strPath);
2547     CommitTransaction();
2548
2549     if (!bKeepId)
2550       AnnounceRemove("musicvideo", idMVideo);
2551   }
2552   catch (...)
2553   {
2554     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2555   }
2556 }
2557
2558 void CVideoDatabase::DeleteStreamDetails(int idFile)
2559 {
2560     m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
2561 }
2562
2563 void CVideoDatabase::DeleteSet(int idSet)
2564 {
2565   try
2566   {
2567     if (NULL == m_pDB.get()) return ;
2568     if (NULL == m_pDS.get()) return ;
2569
2570     CStdString strSQL;
2571     strSQL=PrepareSQL("delete from sets where idSet=%i", idSet);
2572     m_pDS->exec(strSQL.c_str());
2573     strSQL=PrepareSQL("delete from setlinkmovie where idSet=%i", idSet);
2574     m_pDS->exec(strSQL.c_str());
2575   }
2576   catch (...)
2577   {
2578     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
2579   }
2580 }
2581
2582 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
2583 {
2584   for (int i = min + 1; i < max; i++)
2585   {
2586     switch (offsets[i].type)
2587     {
2588     case VIDEODB_TYPE_STRING:
2589       *(CStdString*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+idxOffset).get_asString();
2590       break;
2591     case VIDEODB_TYPE_INT:
2592     case VIDEODB_TYPE_COUNT:
2593       *(int*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+idxOffset).get_asInt();
2594       break;
2595     case VIDEODB_TYPE_BOOL:
2596       *(bool*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+idxOffset).get_asBool();
2597       break;
2598     case VIDEODB_TYPE_FLOAT:
2599       *(float*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+idxOffset).get_asFloat();
2600       break;
2601     }
2602   }
2603 }
2604
2605 DWORD movieTime = 0;
2606 DWORD castTime = 0;
2607
2608 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
2609 {
2610   CVideoInfoTag details;
2611   details.Reset();
2612
2613   switch (type)
2614   {
2615     case VIDEODB_CONTENT_MOVIES:
2616       GetMovieInfo("", details, id);
2617       break;
2618     case VIDEODB_CONTENT_TVSHOWS:
2619       GetTvShowInfo("", details, id);
2620       break;
2621     case VIDEODB_CONTENT_EPISODES:
2622       GetEpisodeInfo("", details, id);
2623       break;
2624     case VIDEODB_CONTENT_MUSICVIDEOS:
2625       GetMusicVideoInfo("", details, id);
2626     default:
2627       break;
2628   }
2629
2630   return details;
2631 }
2632
2633 bool CVideoDatabase::GetStreamDetailsForFileId(CStreamDetails& details, int idFile) const
2634 {
2635   if (idFile < 0)
2636     return false;
2637
2638   bool retVal = false;
2639
2640   dbiplus::Dataset *pDS = m_pDB->CreateDataset();
2641   CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", idFile);
2642   pDS->query(strSQL);
2643
2644   details.Reset();
2645   while (!pDS->eof())
2646   {
2647     CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
2648     switch (e)
2649     {
2650     case CStreamDetail::VIDEO:
2651       {
2652         CStreamDetailVideo *p = new CStreamDetailVideo();
2653         p->m_strCodec = pDS->fv(2).get_asString();
2654         p->m_fAspect = pDS->fv(3).get_asFloat();
2655         p->m_iWidth = pDS->fv(4).get_asInt();
2656         p->m_iHeight = pDS->fv(5).get_asInt();
2657         p->m_iDuration = pDS->fv(10).get_asInt();
2658         details.AddStream(p);
2659         retVal = true;
2660         break;
2661       }
2662     case CStreamDetail::AUDIO:
2663       {
2664         CStreamDetailAudio *p = new CStreamDetailAudio();
2665         p->m_strCodec = pDS->fv(6).get_asString();
2666         if (pDS->fv(7).get_isNull())
2667           p->m_iChannels = -1;
2668         else
2669           p->m_iChannels = pDS->fv(7).get_asInt();
2670         p->m_strLanguage = pDS->fv(8).get_asString();
2671         details.AddStream(p);
2672         retVal = true;
2673         break;
2674       }
2675     case CStreamDetail::SUBTITLE:
2676       {
2677         CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
2678         p->m_strLanguage = pDS->fv(9).get_asString();
2679         details.AddStream(p);
2680         retVal = true;
2681         break;
2682       }
2683     }
2684
2685     pDS->next();
2686   }
2687
2688   pDS->close();
2689   details.DetermineBestStreams();
2690
2691   return retVal;
2692 }
2693
2694 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2695 {
2696   CVideoInfoTag details;
2697   details.Reset();
2698
2699   DWORD time = CTimeUtils::GetTimeMS();
2700   int idMovie = pDS->fv(0).get_asInt();
2701
2702   GetDetailsFromDB(pDS, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
2703
2704   details.m_iDbId = idMovie;
2705   GetCommonDetails(pDS, details);
2706   movieTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2707
2708   GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2709
2710   if (needsCast)
2711   {
2712     // create cast string
2713     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);
2714     m_pDS2->query(strSQL.c_str());
2715     while (!m_pDS2->eof())
2716     {
2717       SActorInfo info;
2718       info.strName = m_pDS2->fv("actors.strActor").get_asString();
2719       info.strRole = m_pDS2->fv("actorlinkmovie.strRole").get_asString();
2720       info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2721       details.m_cast.push_back(info);
2722       m_pDS2->next();
2723     }
2724     castTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2725     details.m_strPictureURL.Parse();
2726
2727     // create sets string
2728     strSQL = PrepareSQL("SELECT sets.strSet FROM sets,setlinkmovie WHERE setlinkmovie.idMovie=%i AND setlinkmovie.idSet=sets.idSet ORDER BY sets.idSet",idMovie);
2729     m_pDS2->query(strSQL.c_str());
2730     while (!m_pDS2->eof())
2731     {
2732       CStdString setName = m_pDS2->fv("sets.strSet").get_asString();
2733       if (!details.m_strSet.IsEmpty())
2734         details.m_strSet += g_advancedSettings.m_videoItemSeparator;
2735       details.m_strSet += setName;
2736       m_pDS2->next();
2737     }
2738
2739     // create tvshowlink string
2740     vector<int> links;
2741     GetLinksToTvShow(idMovie,links);
2742     for (unsigned int i=0;i<links.size();++i)
2743     {
2744       strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
2745                          VIDEODB_ID_TV_TITLE,links[i]);
2746       m_pDS2->query(strSQL.c_str());
2747       if (!m_pDS2->eof())
2748       {
2749         if (!details.m_strShowLink.IsEmpty())
2750           details.m_strShowLink += g_advancedSettings.m_videoItemSeparator;
2751         details.m_strShowLink += m_pDS2->fv(0).get_asString();
2752       }
2753     }
2754     m_pDS2->close();
2755   }
2756   return details;
2757 }
2758
2759 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2760 {
2761   CVideoInfoTag details;
2762   details.Reset();
2763
2764   DWORD time = CTimeUtils::GetTimeMS();
2765   int idTvShow = pDS->fv(0).get_asInt();
2766
2767   GetDetailsFromDB(pDS, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
2768   details.m_iDbId = idTvShow;
2769   details.m_strPath = pDS->fv(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
2770   details.m_iEpisode = m_pDS->fv(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
2771   details.m_playCount = m_pDS->fv(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
2772   details.m_strShowTitle = details.m_strTitle;
2773
2774   movieTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2775
2776   if (needsCast)
2777   {
2778     // create cast string
2779     CStdString strSQL = PrepareSQL("select actors.strActor,actorlinktvshow.strRole,actors.strThumb from actorlinktvshow,actors where actorlinktvshow.idShow=%i and actorlinktvshow.idActor = actors.idActor",idTvShow);
2780     m_pDS2->query(strSQL.c_str());
2781     while (!m_pDS2->eof())
2782     {
2783       SActorInfo info;
2784       info.strName = m_pDS2->fv("actors.strActor").get_asString();
2785       info.strRole = m_pDS2->fv("actorlinktvshow.strRole").get_asString();
2786       info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2787       details.m_cast.push_back(info);
2788       m_pDS2->next();
2789     }
2790     castTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2791     details.m_strPictureURL.Parse();
2792   }
2793   return details;
2794 }
2795
2796 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2797 {
2798   CVideoInfoTag details;
2799   details.Reset();
2800
2801   DWORD time = CTimeUtils::GetTimeMS();
2802   int idEpisode = pDS->fv(0).get_asInt();
2803
2804   GetDetailsFromDB(pDS, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
2805   details.m_iDbId = idEpisode;
2806   GetCommonDetails(pDS, details);
2807   movieTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2808
2809   details.m_strMPAARating = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
2810   details.m_strShowTitle = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
2811   details.m_strStudio = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString();
2812   details.m_strPremiered = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString();
2813
2814   GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2815
2816   if (needsCast)
2817   {
2818     set<int> actors;
2819     set<int>::iterator it;
2820
2821     // create cast string
2822     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);
2823     m_pDS2->query(strSQL.c_str());
2824     bool showCast=false;
2825     while (!m_pDS2->eof() || !showCast)
2826     {
2827       if (!m_pDS2->eof())
2828       {
2829         int idActor = m_pDS2->fv("actors.idActor").get_asInt();
2830         it = actors.find(idActor);
2831
2832         if (it == actors.end())
2833         {
2834           SActorInfo info;
2835           info.strName = m_pDS2->fv("actors.strActor").get_asString();
2836           info.strRole = m_pDS2->fv("actorlinkepisode.strRole").get_asString();
2837           info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2838           details.m_cast.push_back(info);
2839           actors.insert(idActor);
2840         }
2841         m_pDS2->next();
2842       }
2843       if (m_pDS2->eof() && !showCast)
2844       {
2845         showCast = true;
2846         int idShow = GetTvShowForEpisode(details.m_iDbId);
2847         if (idShow > -1)
2848         {
2849           strSQL = PrepareSQL("select actors.idActor,actors.strActor,actorlinktvshow.strRole,actors.strThumb from actorlinktvshow,actors where actorlinktvshow.idShow=%i and actorlinktvshow.idActor = actors.idActor",idShow);
2850           m_pDS2->query(strSQL.c_str());
2851         }
2852       }
2853     }
2854     castTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2855     details.m_strPictureURL.Parse();
2856     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);
2857     m_pDS2->query(strSQL.c_str());
2858     if (!m_pDS2->eof())
2859       details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
2860     m_pDS2->close();
2861   }
2862   return details;
2863 }
2864
2865 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS)
2866 {
2867   CVideoInfoTag details;
2868   details.Reset();
2869
2870   DWORD time = CTimeUtils::GetTimeMS();
2871   int idMovie = pDS->fv(0).get_asInt();
2872
2873   GetDetailsFromDB(pDS, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
2874   details.m_iDbId = idMovie;
2875   GetCommonDetails(pDS, details);
2876   movieTime += CTimeUtils::GetTimeMS() - time; time = CTimeUtils::GetTimeMS();
2877
2878   GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2879
2880   details.m_strPictureURL.Parse();
2881   return details;
2882 }
2883
2884 void CVideoDatabase::GetCommonDetails(auto_ptr<Dataset> &pDS, CVideoInfoTag &details)
2885 {
2886   details.m_iFileId = pDS->fv(VIDEODB_DETAILS_FILEID).get_asInt();
2887   details.m_strPath = pDS->fv(VIDEODB_DETAILS_PATH).get_asString();
2888   CStdString strFileName = pDS->fv(VIDEODB_DETAILS_FILE).get_asString();
2889   ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
2890   details.m_playCount = pDS->fv(VIDEODB_DETAILS_PLAYCOUNT).get_asInt();
2891   details.m_lastPlayed = pDS->fv(VIDEODB_DETAILS_LASTPLAYED).get_asString();
2892 }
2893
2894 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
2895 /// \retval Returns true if the settings exist, false otherwise.
2896 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
2897 {
2898   try
2899   {
2900     // obtain the FileID (if it exists)
2901 #ifdef NEW_VIDEODB_METHODS
2902     if (NULL == m_pDB.get()) return false;
2903     if (NULL == m_pDS.get()) return false;
2904     CStdString strPath, strFileName;
2905     CUtil::Split(strFilenameAndPath, strPath, strFileName);
2906     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());
2907 #else
2908     int idFile = GetFileId(strFilenameAndPath);
2909     if (idFile < 0) return false;
2910     if (NULL == m_pDB.get()) return false;
2911     if (NULL == m_pDS.get()) return false;
2912     // ok, now obtain the settings for this file
2913     CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
2914 #endif
2915     m_pDS->query( strSQL.c_str() );
2916     if (m_pDS->num_rows() > 0)
2917     { // get the video settings info
2918       settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
2919       settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
2920       settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
2921       settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
2922       settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
2923       settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
2924       settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
2925       settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
2926       settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
2927       settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
2928       settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
2929       settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
2930       settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
2931       settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
2932       settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
2933       settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
2934       settings.m_Crop = m_pDS->fv("Crop").get_asBool();
2935       settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
2936       settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
2937       settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
2938       settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
2939       settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
2940       settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
2941       settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
2942       settings.m_SubtitleCached = false;
2943       m_pDS->close();
2944       return true;
2945     }
2946     m_pDS->close();
2947   }
2948   catch (...)
2949   {
2950     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2951   }
2952   return false;
2953 }
2954
2955 /// \brief Sets the settings for a particular video file
2956 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
2957 {
2958   try
2959   {
2960     if (NULL == m_pDB.get()) return ;
2961     if (NULL == m_pDS.get()) return ;
2962     int idFile = AddFile(strFilenameAndPath);
2963     if (idFile < 0)
2964       return;
2965     CStdString strSQL;
2966     strSQL.Format("select * from settings where idFile=%i", idFile);
2967     m_pDS->query( strSQL.c_str() );
2968     if (m_pDS->num_rows() > 0)
2969     {
2970       m_pDS->close();
2971       // update the item
2972       strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,"
2973                        "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
2974                        "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,",
2975                        setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio,
2976                        setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
2977                        setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
2978                        setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess);
2979       CStdString strSQL2;
2980       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);
2981       strSQL += strSQL2;
2982       m_pDS->exec(strSQL.c_str());
2983       return ;
2984     }
2985     else
2986     { // add the items
2987       m_pDS->close();
2988       strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio,"
2989                 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
2990                 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
2991                 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
2992                 "Sharpness,NoiseReduction,NonLinStretch,PostProcess) "
2993               "VALUES ";
2994       strSQL += PrepareSQL("(%i,%i,%i,%f,%f,%i,%i,%f,%i,%f,%f,%f,%f,%f,%i,%i,%i,%i,%i,%i,%i,%f,%f,%i,%i)",
2995                            idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio,
2996                            setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
2997                            setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
2998                            setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
2999                            setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess);
3000       m_pDS->exec(strSQL.c_str());
3001     }
3002   }
3003   catch (...)
3004   {
3005     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3006   }
3007 }
3008
3009 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3010 /// \retval Returns true if the stack times exist, false otherwise.
3011 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> &times)
3012 {
3013   try
3014   {
3015     // obtain the FileID (if it exists)
3016     int idFile = GetFileId(filePath);
3017     if (idFile < 0) return false;
3018     if (NULL == m_pDB.get()) return false;
3019     if (NULL == m_pDS.get()) return false;
3020     // ok, now obtain the settings for this file
3021     CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3022     m_pDS->query( strSQL.c_str() );
3023     if (m_pDS->num_rows() > 0)
3024     { // get the video settings info
3025       CStdStringArray timeString;
3026       int timeTotal = 0;
3027       StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3028       times.clear();
3029       for (unsigned int i = 0; i < timeString.size(); i++)
3030       {
3031         times.push_back(atoi(timeString[i].c_str()));
3032         timeTotal += atoi(timeString[i].c_str());
3033       }
3034       m_pDS->close();
3035       return (timeTotal > 0);
3036     }
3037     m_pDS->close();
3038   }
3039   catch (...)
3040   {
3041     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3042   }
3043   return false;
3044 }
3045
3046 /// \brief Sets the stack times for a particular video file
3047 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> &times)
3048 {
3049   try
3050   {
3051     if (NULL == m_pDB.get()) return ;
3052     if (NULL == m_pDS.get()) return ;
3053     int idFile = AddFile(filePath);
3054     if (idFile < 0)
3055       return;
3056
3057     // delete any existing items
3058     m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3059
3060     // add the items
3061     CStdString timeString;
3062     timeString.Format("%i", times[0]);
3063     for (unsigned int i = 1; i < times.size(); i++)
3064     {
3065       CStdString time;
3066       time.Format(",%i", times[i]);
3067       timeString += time;
3068     }
3069     m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3070   }
3071   catch (...)
3072   {
3073     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3074   }
3075 }
3076
3077 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
3078 {
3079   if(CUtil::IsMultiPath(strPath))
3080   {
3081     vector<CStdString> paths;
3082     CMultiPathDirectory::GetPaths(strPath, paths);
3083
3084     for(unsigned i=0;i<paths.size();i++)
3085       RemoveContentForPath(paths[i], progress);
3086   }
3087
3088   try
3089   {
3090     if (NULL == m_pDB.get()) return ;
3091     if (NULL == m_pDS.get()) return ;
3092
3093     auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3094     CStdString strPath1(strPath);
3095     CStdString strSQL = PrepareSQL("select idPath,strContent,strPath from path where strPath like '%%%s%%'",strPath1.c_str());
3096     progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
3097     pDS->query(strSQL.c_str());
3098     if (progress)
3099     {
3100       progress->SetHeading(700);
3101       progress->SetLine(0, "");
3102       progress->SetLine(1, 313);
3103       progress->SetLine(2, 330);
3104       progress->SetPercentage(0);
3105       progress->StartModal();
3106       progress->ShowProgressBar(true);
3107     }
3108     int iCurr=0;
3109     int iMax = pDS->num_rows();
3110     while (!pDS->eof())
3111     {
3112       bool bMvidsChecked=false;
3113       if (progress)
3114       {
3115         progress->SetPercentage((int)((float)(iCurr++)/iMax*100.f));
3116         progress->Progress();
3117       }
3118       int idPath = pDS->fv("path.idPath").get_asInt();
3119       CStdString strCurrPath = pDS->fv("path.strPath").get_asString();
3120       if (HasTvShowInfo(strCurrPath))
3121         DeleteTvShow(strCurrPath);
3122       else
3123       {
3124         strSQL=PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i",idPath);
3125         m_pDS2->query(strSQL.c_str());
3126         if (m_pDS2->eof())
3127         {
3128           strSQL=PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i",idPath);
3129           m_pDS2->query(strSQL.c_str());
3130           bMvidsChecked = true;
3131         }
3132         while (!m_pDS2->eof())
3133         {
3134           CStdString strMoviePath;
3135           CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
3136           ConstructPath(strMoviePath, strCurrPath, strFileName);
3137           if (HasMovieInfo(strMoviePath))
3138             DeleteMovie(strMoviePath);
3139           if (HasMusicVideoInfo(strMoviePath))
3140             DeleteMusicVideo(strMoviePath);
3141           m_pDS2->next();
3142           if (m_pDS2->eof() && !bMvidsChecked)
3143           {
3144             strSQL=PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i",idPath);
3145             m_pDS2->query(strSQL.c_str());
3146             bMvidsChecked = true;
3147           }
3148         }
3149         m_pDS2->close();
3150       }
3151       pDS->next();
3152     }
3153     strSQL = PrepareSQL("update path set strContent = '', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where strPath like '%%%s%%'",strPath1.c_str());
3154     pDS->exec(strSQL.c_str());
3155   }
3156   catch (...)
3157   {
3158     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
3159   }
3160   if (progress)
3161     progress->Close();
3162 }
3163
3164 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
3165 {
3166   // if we have a multipath, set scraper for all contained paths too
3167   if(CUtil::IsMultiPath(filePath))
3168   {
3169     vector<CStdString> paths;
3170     CMultiPathDirectory::GetPaths(filePath, paths);
3171
3172     for(unsigned i=0;i<paths.size();i++)
3173       SetScraperForPath(paths[i],scraper,settings);
3174   }
3175
3176   try
3177   {
3178     if (NULL == m_pDB.get()) return ;
3179     if (NULL == m_pDS.get()) return ;
3180     int idPath = GetPathId(filePath);
3181     if (idPath < 0)
3182     { // no path found - we have to add one
3183       idPath = AddPath(filePath);
3184       if (idPath < 0) return ;
3185     }
3186
3187     // Update
3188     CStdString strSQL;
3189     if (settings.exclude)
3190     { //NB See note in ::GetScraperForPath about strContent=='none'
3191       strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
3192     }
3193     else if(!scraper)
3194     { // catch clearing content, but not excluding
3195       strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
3196     }
3197     else
3198     {
3199       CStdString content = TranslateContent(scraper->Content());
3200       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);
3201     }
3202     m_pDS->exec(strSQL.c_str());
3203   }
3204   catch (...)
3205   {
3206     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3207   }
3208 }
3209
3210 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
3211 {
3212   try
3213   {
3214     if (NULL == m_pDB.get()) return false;
3215     if (NULL == m_pDS.get()) return false;
3216
3217     CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
3218     if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
3219       return false;
3220     bool found = m_pDS->fv(0).get_asInt() > 0;
3221     m_pDS->close();
3222     return found;
3223   }
3224   catch (...)
3225   {
3226     CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
3227   }
3228   return false;
3229 }
3230
3231 bool CVideoDatabase::UpdateOldVersion(int iVersion)
3232 {
3233   BeginTransaction();
3234
3235   try
3236   {
3237     if (iVersion < 35)
3238     {
3239       m_pDS->exec("alter table settings add NonLinStretch bool");
3240     }
3241     if (iVersion < 36)
3242     {
3243       m_pDS->exec("alter table path add exclude bool");
3244     }
3245     if (iVersion < 37)
3246     {
3247       //recreate tables
3248       CStdString columnsSelect, columns;
3249       for (int i = 0; i < 21; i++)
3250       {
3251         CStdString column;
3252         column.Format(",c%02d", i);
3253         columnsSelect += column;
3254         columns += column + " text";
3255       }
3256
3257       //movie
3258       m_pDS->exec(PrepareSQL("CREATE TABLE movienew ( idMovie integer primary key, idFile integer%s)", columns.c_str()));
3259       m_pDS->exec(PrepareSQL("INSERT INTO movienew select idMovie,idFile%s from movie", columnsSelect.c_str()));
3260       m_pDS->exec("DROP TABLE movie");
3261       m_pDS->exec("ALTER TABLE movienew RENAME TO movie");
3262
3263       m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
3264       m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
3265
3266       //episode
3267       m_pDS->exec(PrepareSQL("CREATE TABLE episodenew ( idEpisode integer primary key, idFile integer%s)", columns.c_str()));
3268       m_pDS->exec(PrepareSQL("INSERT INTO episodenew select idEpisode,idFile%s from episode", columnsSelect.c_str()));
3269       m_pDS->exec("DROP TABLE episode");
3270       m_pDS->exec("ALTER TABLE episodenew RENAME TO episode");
3271
3272       m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
3273       m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
3274       m_pDS->exec(PrepareSQL("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE));
3275       m_pDS->exec(PrepareSQL("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK));
3276
3277       //musicvideo
3278       m_pDS->exec(PrepareSQL("CREATE TABLE musicvideonew ( idMVideo integer primary key, idFile integer%s)", columns.c_str()));
3279       m_pDS->exec(PrepareSQL("INSERT INTO musicvideonew select idMVideo,idFile%s from musicvideo", columnsSelect.c_str()));
3280       m_pDS->exec("DROP TABLE musicvideo");
3281       m_pDS->exec("ALTER TABLE musicvideonew RENAME TO musicvideo");
3282
3283       m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
3284       m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
3285     }
3286     if (iVersion < 38)
3287     {
3288       m_pDS->exec("ALTER table movie add c21 text");
3289       m_pDS->exec("ALTER table episode add c21 text");
3290       m_pDS->exec("ALTER table musicvideo add c21 text");
3291       m_pDS->exec("ALTER table tvshow add c21 text");
3292
3293       CLog::Log(LOGINFO, "create country table");
3294       m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
3295
3296       CLog::Log(LOGINFO, "create countrylinkmovie table");
3297       m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
3298       m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
3299       m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
3300     }
3301     if (iVersion < 39)
3302     { // update for old scrapers
3303       m_pDS->query("select idPath,strScraper from path");
3304       set<CStdString> scrapers;
3305       while (!m_pDS->eof())
3306       {
3307         // translate the addon
3308         CStdString scraperID = ADDON::UpdateVideoScraper(m_pDS->fv(1).get_asString());
3309         if (!scraperID.IsEmpty())
3310         {
3311           scrapers.insert(scraperID);
3312           CStdString update = PrepareSQL("update path set strScraper='%s' where idPath=%i", scraperID.c_str(), m_pDS->fv(0).get_asInt());
3313           m_pDS2->exec(update);
3314         }
3315         m_pDS->next();
3316       }
3317       m_pDS->close();
3318       // ensure these scrapers are installed
3319       CGUIWindowAddonBrowser::InstallAddonsFromXBMCRepo(scrapers);
3320     }
3321     if (iVersion < 40)
3322     {
3323       m_pDS->exec("DELETE FROM streamdetails");
3324       m_pDS->exec("ALTER table streamdetails add iVideoDuration integer");
3325     }
3326     if (iVersion < 41)
3327     {
3328       m_pDS->exec("ALTER table settings add PostProcess bool");
3329     }
3330     if (iVersion < 42)
3331     {
3332       m_pDS->exec("DELETE FROM streamdetails"); //Roll the stream details as changed from minutes to seconds
3333     }
3334   }
3335   catch (...)
3336   {
3337     CLog::Log(LOGERROR, "Error attempting to update the database version!");
3338     RollbackTransaction();
3339     return false;
3340   }
3341   CommitTransaction();
3342   return true;
3343 }
3344
3345 int CVideoDatabase::GetPlayCount(const CFileItem &item)
3346 {
3347   int id = GetFileId(item);
3348   if (id < 0)
3349     return 0;  // not in db, so not watched
3350
3351   try
3352   {
3353     // error!
3354     if (NULL == m_pDB.get()) return -1;
3355     if (NULL == m_pDS.get()) return -1;
3356
3357     CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
3358     int count = 0;
3359     if (m_pDS->query(strSQL.c_str()))
3360     {
3361       // there should only ever be one row returned
3362       if (m_pDS->num_rows() == 1)
3363         count = m_pDS->fv(0).get_asInt();
3364       m_pDS->close();
3365     }
3366     return count;
3367   }
3368   catch (...)
3369   {
3370     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3371   }
3372   return -1;
3373 }
3374
3375 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
3376 {
3377   if (NULL == m_pDB.get()) return;
3378   if (NULL == m_pDS.get()) return;
3379   if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
3380
3381   CStdString exec;
3382   if (type == VIDEODB_CONTENT_TVSHOWS)
3383     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);
3384   else if (type == VIDEODB_CONTENT_MOVIES)
3385     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);
3386
3387   try
3388   {
3389     m_pDS->exec(exec.c_str());
3390
3391     if (type == VIDEODB_CONTENT_TVSHOWS)
3392       AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
3393     else if (type == VIDEODB_CONTENT_MOVIES)
3394       AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
3395   }
3396   catch (...)
3397   {
3398     CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.m_strPath.c_str());
3399   }
3400 }
3401
3402 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CStdString &date)
3403 {
3404   int id = AddFile(item);
3405   if (id < 0)
3406     return;
3407
3408   // and mark as watched
3409   try
3410   {
3411     if (NULL == m_pDB.get()) return ;
3412     if (NULL == m_pDS.get()) return ;
3413
3414     CStdString strSQL;
3415     if (count)
3416     {
3417       if (date.IsEmpty())
3418         strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
3419       else
3420         strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.c_str(), id);
3421     }
3422     else
3423     {
3424       if (date.IsEmpty())
3425         strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
3426       else
3427         strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.c_str(), id);
3428     }
3429
3430     m_pDS->exec(strSQL.c_str());
3431
3432     CVariant data;
3433     data["playcount"] = count;
3434     ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Library, "xbmc", "NewPlayCount", CFileItemPtr(new CFileItem(item)), data);
3435   }
3436   catch (...)
3437   {
3438     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3439   }
3440 }
3441
3442 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
3443 {
3444   SetPlayCount(item, GetPlayCount(item) + 1);
3445 }
3446
3447 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
3448 {
3449   SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime().GetAsDBDateTime());
3450 }
3451
3452 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
3453 {
3454   try
3455   {
3456     if (NULL == m_pDB.get()) return ;
3457     if (NULL == m_pDS.get()) return ;
3458     CStdString content;
3459     CStdString strSQL;
3460     if (iType == VIDEODB_CONTENT_MOVIES)
3461     {
3462       CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3463       strSQL = PrepareSQL("UPDATE movie SET c%02d='%s' WHERE idMovie=%i", VIDEODB_ID_TITLE, strNewMovieTitle.c_str(), idMovie );
3464       content = "movie";
3465     }
3466     else if (iType == VIDEODB_CONTENT_EPISODES)
3467     {
3468       CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3469       strSQL = PrepareSQL("UPDATE episode SET c%02d='%s' WHERE idEpisode=%i", VIDEODB_ID_EPISODE_TITLE, strNewMovieTitle.c_str(), idMovie );
3470       content = "episode";
3471     }
3472     else if (iType == VIDEODB_CONTENT_TVSHOWS)
3473     {
3474       CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3475       strSQL = PrepareSQL("UPDATE tvshow SET c%02d='%s' WHERE idShow=%i", VIDEODB_ID_TV_TITLE, strNewMovieTitle.c_str(), idMovie );
3476       content = "tvshow";
3477     }
3478     else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
3479     {
3480       CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3481       strSQL = PrepareSQL("UPDATE musicvideo SET c%02d='%s' WHERE idMVideo=%i", VIDEODB_ID_MUSICVIDEO_TITLE, strNewMovieTitle.c_str(), idMovie );
3482       content = "musicvideo";
3483     }
3484     else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
3485     {
3486       CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
3487       strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
3488     }
3489     m_pDS->exec(strSQL.c_str());
3490
3491     if (content.size() > 0)
3492       AnnounceUpdate(content, idMovie);
3493   }
3494   catch (...)
3495   {
3496     CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
3497   }
3498 }
3499
3500 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
3501 void CVideoDatabase::EraseVideoSettings()
3502 {
3503   try
3504   {
3505     CLog::Log(LOGINFO, "Deleting settings information for all movies");
3506     m_pDS->exec("delete from settings");
3507   }
3508   catch (...)
3509   {
3510     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3511   }
3512 }
3513
3514 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
3515 {
3516   return GetNavCommon(strBaseDir, items, "genre", idContent);
3517 }
3518
3519 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
3520 {
3521   return GetNavCommon(strBaseDir, items, "country", idContent);
3522 }
3523
3524 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent)
3525 {
3526   return GetNavCommon(strBaseDir, items, "studio", idContent);
3527 }
3528
3529 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent)
3530 {
3531   try
3532   {
3533     if (NULL == m_pDB.get()) return false;
3534     if (NULL == m_pDS.get()) return false;
3535
3536     CStdString strSQL;
3537     if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
3538     {
3539       if (idContent == VIDEODB_CONTENT_MOVIES)
3540         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",
3541                            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());
3542       else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
3543         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",
3544                            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());
3545       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3546         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",
3547                            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());
3548     }
3549     else
3550     {
3551       if (idContent == VIDEODB_CONTENT_MOVIES)
3552         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",
3553                            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());
3554       else if (idContent == VIDEODB_CONTENT_TVSHOWS)
3555         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",
3556                            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());
3557       else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
3558         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",
3559                            type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(),