Merged from linuxport rev21200:21590. Updated.
[xbmc:xbmc-antiquated.git] / XBMC / 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 "stdafx.h"
23 #include "VideoDatabase.h"
24 #include "GUIWindowVideoBase.h"
25 #include "utils/fstrcmp.h"
26 #include "utils/RegExp.h"
27 #include "utils/GUIInfoManager.h"
28 #include "Util.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 "GUIDialogProgress.h"
38 #include "GUIDialogYesNo.h"
39 #include "FileItem.h"
40
41 using namespace std;
42 using namespace dbiplus;
43 using namespace XFILE;
44 using namespace DIRECTORY;
45 using namespace VIDEO;
46
47 #define VIDEO_DATABASE_VERSION 28
48 #define VIDEO_DATABASE_OLD_VERSION 3.f
49 #define VIDEO_DATABASE_NAME "MyVideos34.db"
50
51 CBookmark::CBookmark()
52 {
53   episodeNumber = 0;
54   seasonNumber = 0;
55   timeInSeconds = 0.0f;
56   type = STANDARD;
57 }
58
59 //********************************************************************************************************************************
60 CVideoDatabase::CVideoDatabase(void)
61 {
62   m_preV2version=VIDEO_DATABASE_OLD_VERSION;
63   m_version = VIDEO_DATABASE_VERSION;
64   m_strDatabaseFile=VIDEO_DATABASE_NAME;
65 }
66
67 //********************************************************************************************************************************
68 CVideoDatabase::~CVideoDatabase(void)
69 {}
70
71 //********************************************************************************************************************************
72 bool CVideoDatabase::CreateTables()
73 {
74   /* indexes should be added on any columns that are used in in  */
75   /* a where or a join. primary key on a column is the same as a */
76   /* unique index on that column, so there is no need to add any */
77   /* index if no other columns are refered                       */
78
79   /* order of indexes are important, for an index to be considered all  */
80   /* columns up to the column in question have to have been specified   */
81   /* select * from actorlinkmovie where idMovie = 1, can not take       */
82   /* advantage of a index that has been created on ( idGenre, idMovie ) */
83   /*, hower on on ( idMovie, idGenre ) will be considered for use       */
84
85   BeginTransaction();
86   try
87   {
88     CDatabase::CreateTables();
89
90     CLog::Log(LOGINFO, "create bookmark table");
91     m_pDS->exec("CREATE TABLE bookmark ( idBookmark integer primary key, idFile integer, timeInSeconds double, thumbNailImage text, player text, playerState text, type integer)\n");
92     m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile)");
93
94     CLog::Log(LOGINFO, "create settings table");
95     m_pDS->exec("CREATE TABLE settings ( idFile integer, Interleaved bool, NoCache bool, Deinterlace bool, FilmGrain float,"
96                 "ViewMode integer,ZoomAmount float, PixelRatio float, AudioStream integer, SubtitleStream integer,"
97                 "SubtitleDelay float, SubtitlesOn bool, Brightness float, Contrast float, Gamma float,"
98                 "VolumeAmplification float, AudioDelay float, OutputToAllSpeakers bool, ResumeTime integer, Crop bool, CropLeft integer,"
99                 "CropRight integer, CropTop integer, CropBottom integer, Sharpness float, NoiseReduction float)\n");
100     m_pDS->exec("CREATE UNIQUE INDEX ix_settings ON settings ( idFile )\n");
101
102     CLog::Log(LOGINFO, "create stacktimes table");
103     m_pDS->exec("CREATE TABLE stacktimes (idFile integer, times text)\n");
104     m_pDS->exec("CREATE UNIQUE INDEX ix_stacktimes ON stacktimes ( idFile )\n");
105
106     CLog::Log(LOGINFO, "create genre table");
107     m_pDS->exec("CREATE TABLE genre ( idGenre integer primary key, strGenre text)\n");
108
109     CLog::Log(LOGINFO, "create genrelinkmovie table");
110     m_pDS->exec("CREATE TABLE genrelinkmovie ( idGenre integer, idMovie integer)\n");
111     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_1 ON genrelinkmovie ( idGenre, idMovie)\n");
112     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_2 ON genrelinkmovie ( idMovie, idGenre)\n");
113
114     CLog::Log(LOGINFO, "create movie table");
115     CStdString columns = "CREATE TABLE movie ( idMovie integer primary key";
116     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
117     {
118       CStdString column;
119       column.Format(",c%02d text", i);
120       columns += column;
121     }
122     columns += ",idFile integer)";
123     m_pDS->exec(columns.c_str());
124     m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
125     m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
126
127     CLog::Log(LOGINFO, "create actorlinkmovie table");
128     m_pDS->exec("CREATE TABLE actorlinkmovie ( idActor integer, idMovie integer, strRole text)\n");
129     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_1 ON actorlinkmovie ( idActor, idMovie )\n");
130     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_2 ON actorlinkmovie ( idMovie, idActor )\n");
131
132     CLog::Log(LOGINFO, "create directorlinkmovie table");
133     m_pDS->exec("CREATE TABLE directorlinkmovie ( idDirector integer, idMovie integer)\n");
134     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_1 ON directorlinkmovie ( idDirector, idMovie )\n");
135     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_2 ON directorlinkmovie ( idMovie, idDirector )\n");
136
137     CLog::Log(LOGINFO, "create writerlinkmovie table");
138     m_pDS->exec("CREATE TABLE writerlinkmovie ( idWriter integer, idMovie integer)\n");
139     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_1 ON writerlinkmovie ( idWriter, idMovie )\n");
140     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_2 ON writerlinkmovie ( idMovie, idWriter )\n");
141
142     CLog::Log(LOGINFO, "create actors table");
143     m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
144
145     CLog::Log(LOGINFO, "create path table");
146     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)\n");
147     m_pDS->exec("CREATE UNIQUE INDEX ix_path ON path ( strPath )\n");
148
149     CLog::Log(LOGINFO, "create files table");
150     m_pDS->exec("CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text)\n");
151     m_pDS->exec("CREATE UNIQUE INDEX ix_files ON files ( idPath, strFilename )\n");
152
153     CLog::Log(LOGINFO, "create tvshow table");
154     columns = "CREATE TABLE tvshow ( idShow integer primary key";
155     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
156     {
157       CStdString column;
158       column.Format(",c%02d text", i);
159       columns += column;
160     }
161     columns += ")";
162     m_pDS->exec(columns.c_str());
163
164     CLog::Log(LOGINFO, "create directorlinktvshow table");
165     m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
166     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
167     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
168
169     CLog::Log(LOGINFO, "create actorlinktvshow table");
170     m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text)\n");
171     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
172     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
173
174     CLog::Log(LOGINFO, "create studiolinktvshow table");
175     m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
176     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
177     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
178
179     CLog::Log(LOGINFO, "create episode table");
180     columns = "CREATE TABLE episode ( idEpisode integer primary key";
181     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
182     {
183       CStdString column;
184       column.Format(",c%02d text", i);
185       columns += column;
186     }
187     columns += ",idFile integer)";
188     m_pDS->exec(columns.c_str());
189     m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
190     m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
191     CStdString createColIndex;
192     createColIndex.Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
193     m_pDS->exec(createColIndex.c_str());
194     createColIndex.Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
195     m_pDS->exec(createColIndex.c_str());
196
197     CLog::Log(LOGINFO, "create tvshowlinkepisode table");
198     m_pDS->exec("CREATE TABLE tvshowlinkepisode ( idShow integer, idEpisode integer)\n");
199     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkepisode_1 ON tvshowlinkepisode ( idShow, idEpisode )\n");
200     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkepisode_2 ON tvshowlinkepisode ( idEpisode, idShow )\n");
201
202     CLog::Log(LOGINFO, "create tvshowlinkpath table");
203     m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
204     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
205     m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
206
207     CLog::Log(LOGINFO, "create actorlinkepisode table");
208     m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text)\n");
209     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
210     m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
211
212     CLog::Log(LOGINFO, "create directorlinkepisode table");
213     m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
214     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
215     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
216
217     CLog::Log(LOGINFO, "create writerlinkepisode table");
218     m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
219     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
220     m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
221
222     CLog::Log(LOGINFO, "create genrelinktvshow table");
223     m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
224     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
225     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
226
227     CLog::Log(LOGINFO, "create movielinktvshow table");
228     m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
229     m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
230     m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
231
232     CLog::Log(LOGINFO, "create studio table");
233     m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
234
235     CLog::Log(LOGINFO, "create studiolinkmovie table");
236     m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
237     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
238     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
239
240     CLog::Log(LOGINFO, "create musicvideo table");
241     columns = "CREATE TABLE musicvideo ( idMVideo integer primary key";
242     for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
243     {
244       CStdString column;
245       column.Format(",c%02d text", i);
246       columns += column;
247     }
248     columns += ",idFile integer)";
249     m_pDS->exec(columns.c_str());
250     m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
251     m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
252
253     CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
254     m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
255     m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
256     m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
257
258     CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
259     m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
260     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
261     m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
262
263     CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
264     m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
265     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
266     m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
267
268     CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
269     m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
270     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
271     m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
272
273     CLog::Log(LOGINFO, "create streaminfo table");
274     m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
275       "strVideoCodec text, fVideoAspect real, iVideoWidth integer, iVideoHeight integer, "
276       "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text)");
277     m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
278
279     CLog::Log(LOGINFO, "create tvshowview");
280     CStdString showview=FormatSQL("create view tvshowview as select tvshow.*,path.strPath as strPath,"
281                                   "counts.totalcount as totalCount,counts.watchedcount as watchedCount,"
282                                   "counts.totalcount=counts.watchedcount as watched from tvshow "
283                                   "join tvshowlinkpath on tvshow.idShow=tvshowlinkpath.idShow "
284                                   "join path on path.idpath=tvshowlinkpath.idPath "
285                                   "left outer join ("
286                                   "    select tvshow.idShow as idShow,count(1) as totalcount,count(files.playCount) as watchedcount from tvshow "
287                                   "    join tvshowlinkepisode on tvshow.idShow = tvshowlinkepisode.idShow "
288                                   "    join episode on episode.idEpisode = tvshowlinkepisode.idEpisode "
289                                   "    join files on files.idFile = episode.idFile "
290                                   "    group by tvshow.idShow"
291                                   ") counts on tvshow.idShow = counts.idShow");
292     m_pDS->exec(showview.c_str());
293
294     CLog::Log(LOGINFO, "create episodeview");
295     CStdString episodeview = FormatSQL("create view episodeview as select episode.*,files.strFileName as strFileName,"
296                                        "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,"
297                                        "tvshow.c%02d as premiered from episode "
298                                        "join files on files.idFile=episode.idFile "
299                                        "join tvshowlinkepisode on episode.idepisode=tvshowlinkepisode.idepisode "
300                                        "join tvshow on tvshow.idshow=tvshowlinkepisode.idshow "
301                                        "join path on files.idPath=path.idPath",VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_PREMIERED);
302     m_pDS->exec(episodeview.c_str());
303
304     CLog::Log(LOGINFO, "create musicvideoview");
305     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 "
306                 "from musicvideo join files on files.idFile=musicvideo.idFile join path on path.idPath=files.idPath");
307
308     CLog::Log(LOGINFO, "create movieview");
309     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 "
310                 "from movie join files on files.idFile=movie.idFile join path on path.idPath=files.idPath");
311   }
312   catch (...)
313   {
314     CLog::Log(LOGERROR, "%s unable to create tables:%i", __FUNCTION__, (int)GetLastError());
315     RollbackTransaction();
316     return false;
317   }
318   CommitTransaction();
319   return true;
320 }
321
322 //********************************************************************************************************************************
323 long CVideoDatabase::GetPathId(const CStdString& strPath)
324 {
325   CStdString strSQL;
326   try
327   {
328     long lPathId=-1;
329     if (NULL == m_pDB.get()) return -1;
330     if (NULL == m_pDS.get()) return -1;
331
332     CStdString strPath1(strPath);
333     if (CUtil::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
334       CUtil::GetParentPath(strPath,strPath1);
335
336     CUtil::AddSlashAtEnd(strPath1);
337
338     strSQL=FormatSQL("select idPath from path where strPath like '%s'",strPath1.c_str());
339     m_pDS->query(strSQL.c_str());
340     if (!m_pDS->eof())
341       lPathId = m_pDS->fv("path.idPath").get_asLong();
342
343     m_pDS->close();
344     return lPathId;
345   }
346   catch (...)
347   {
348     CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
349   }
350   return -1;
351 }
352
353 bool CVideoDatabase::GetPaths(map<CStdString,VIDEO::SScanSettings> &paths)
354 {
355   try
356   {
357     if (NULL == m_pDB.get()) return false;
358     if (NULL == m_pDS.get()) return false;
359
360     paths.clear();
361
362     SScanSettings settings;
363     SScraperInfo info;
364
365     memset(&settings, 0, sizeof(settings));
366
367     // grab all paths with movie content set
368     if (!m_pDS->query("select strPath,scanRecursive,useFolderNames,noUpdate from path"
369                       " where (strContent = 'movies' or strContent = 'musicvideos')"
370                       " and strPath NOT like 'multipath://%%'"
371                       " order by strPath"))
372       return false;
373
374     while (!m_pDS->eof())
375     {
376       if (!m_pDS->fv("noUpdate").get_asBool())
377       {
378         CStdString strPath = m_pDS->fv("strPath").get_asString();
379
380         settings.parent_name = m_pDS->fv("useFolderNames").get_asBool();
381         settings.recurse     = m_pDS->fv("scanRecursive").get_asInteger();
382         settings.parent_name_root = settings.parent_name && !settings.recurse;
383
384         paths.insert(pair<CStdString,SScanSettings>(strPath,settings));
385       }
386       m_pDS->next();
387     }
388     m_pDS->close();
389
390     // then grab all tvshow paths
391     if (!m_pDS->query("select strPath,scanRecursive,useFolderNames,strContent,noUpdate from path"
392                       " where ( strContent = 'tvshows'"
393                       "       or idPath in (select idpath from tvshowlinkpath))"
394                       " and strPath NOT like 'multipath://%%'"
395                       " order by strPath"))
396       return false;
397
398     while (!m_pDS->eof())
399     {
400       if (!m_pDS->fv("noUpdate").get_asBool())
401       {
402         CStdString strPath = m_pDS->fv("strPath").get_asString();
403         CStdString strContent = m_pDS->fv("strContent").get_asString();
404         if(strContent.Equals("tvshows"))
405         {
406           settings.parent_name = m_pDS->fv("useFolderNames").get_asBool();
407           settings.recurse     = m_pDS->fv("scanRecursive").get_asInteger();
408           settings.parent_name_root = settings.parent_name && !settings.recurse;
409         }
410         else
411         {
412           settings.parent_name = true;
413           settings.recurse = 0;
414           settings.parent_name_root = true;
415         }
416
417         paths.insert(pair<CStdString,SScanSettings>(strPath,settings));
418       }
419       m_pDS->next();
420     }
421     m_pDS->close();
422
423     // finally grab all other paths holding a movie which is not a stack or a rar archive
424     // - this isnt perfect but it should do fine in most situations.
425     // reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
426     // not making mistakes must take priority
427     if (!m_pDS->query("select strPath,noUpdate from path"
428                        " where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
429                        " and idPath NOT in (select idpath from tvshowlinkpath)"
430                        " and idPath NOT in (select idpath from files where strFileName like 'video_ts.ifo')" // dvdfolders get stacked to a single item in parent folder
431                        " and strPath NOT like 'multipath://%%'"
432                        " and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
433                        " order by strPath"))
434
435       return false;
436     while (!m_pDS->eof())
437     {
438       if (!m_pDS->fv("noUpdate").get_asBool())
439       {
440         CStdString strPath = m_pDS->fv("strPath").get_asString();
441
442         settings.parent_name = false;
443         settings.recurse = 0;
444         settings.parent_name_root = settings.parent_name && !settings.recurse;
445
446         paths.insert(pair<CStdString,SScanSettings>(strPath,settings));
447       }
448       m_pDS->next();
449     }
450     m_pDS->close();
451     return true;
452   }
453   catch (...)
454   {
455     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
456   }
457   return false;
458 }
459
460 bool CVideoDatabase::GetPathsForTvShow(long idShow, vector<long>& paths)
461 {
462   CStdString strSQL;
463   try
464   {
465     if (NULL == m_pDB.get()) return false;
466     if (NULL == m_pDS.get()) return false;
467     strSQL = FormatSQL("select distinct path.idPath from path,tvshowlinkepisode join episode on tvshowlinkepisode.idEpisode=episode.idEpisode join files on files.idPath=path.idPath where episode.idFile = files.idFile and tvshowlinkepisode.idShow=%u",idShow);
468     m_pDS->query(strSQL.c_str());
469     while (!m_pDS->eof())
470     {
471       paths.push_back(m_pDS->fv(0).get_asLong());
472       m_pDS->next();
473     }
474     m_pDS->close();
475     return true;
476   }
477   catch (...)
478   {
479     CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
480   }
481   return false;
482 }
483
484 long CVideoDatabase::AddPath(const CStdString& strPath)
485 {
486   CStdString strSQL;
487   try
488   {
489     long lPathId;
490     if (NULL == m_pDB.get()) return -1;
491     if (NULL == m_pDS.get()) return -1;
492
493     CStdString strPath1(strPath);
494     if (CUtil::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
495       CUtil::GetParentPath(strPath,strPath1);
496
497     CUtil::AddSlashAtEnd(strPath1);
498
499     strSQL=FormatSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
500     m_pDS->exec(strSQL.c_str());
501     lPathId = (long)sqlite3_last_insert_rowid( m_pDB->getHandle() );
502     return lPathId;
503   }
504   catch (...)
505   {
506     CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
507   }
508   return -1;
509 }
510
511 bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
512 {
513   CStdString strSQL;
514   try
515   {
516     if (NULL == m_pDB.get()) return false;
517     if (NULL == m_pDS.get()) return false;
518
519     CStdString strSQL=FormatSQL("select strHash from path where strPath like '%s'", path.c_str());
520     m_pDS->query(strSQL.c_str());
521     if (m_pDS->num_rows() == 0)
522       return false;
523     hash = m_pDS->fv("strHash").get_asString();
524     return true;
525   }
526   catch (...)
527   {
528     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
529   }
530
531   return false;
532 }
533
534 //********************************************************************************************************************************
535 long CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
536 {
537   CStdString strSQL = "";
538   try
539   {
540     long lFileId;
541     if (NULL == m_pDB.get()) return -1;
542     if (NULL == m_pDS.get()) return -1;
543
544     CStdString strFileName, strPath;
545     SplitPath(strFileNameAndPath,strPath,strFileName);
546
547     long lPathId=GetPathId(strPath);
548     if (lPathId < 0)
549       lPathId = AddPath(strPath);
550
551     if (lPathId < 0)
552       return -1;
553
554     CStdString strSQL=FormatSQL("select idFile from files where strFileName like '%s' and idPath=%u", strFileName.c_str(),lPathId);
555
556     m_pDS->query(strSQL.c_str());
557     if (m_pDS->num_rows() > 0)
558     {
559       lFileId = m_pDS->fv("idFile").get_asLong() ;
560       m_pDS->close();
561       return lFileId;
562     }
563     m_pDS->close();
564     strSQL=FormatSQL("insert into files (idFile,idPath,strFileName) values(NULL, %u, '%s')", lPathId,strFileName.c_str());
565     m_pDS->exec(strSQL.c_str());
566     lFileId = (long)sqlite3_last_insert_rowid( m_pDB->getHandle() );
567     return lFileId;
568   }
569   catch (...)
570   {
571     CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
572   }
573   return -1;
574 }
575
576 bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
577 {
578   try
579   {
580     if (NULL == m_pDB.get()) return false;
581     if (NULL == m_pDS.get()) return false;
582
583     if (hash.IsEmpty())
584     { // this is an empty folder - we need only add it to the path table
585       // if the path actually exists
586       if (!CDirectory::Exists(path))
587         return false;
588     }
589     long pathId = GetPathId(path);
590     if (pathId < 0)
591       pathId = AddPath(path);
592     if (pathId < 0) return false;
593
594     CStdString strSQL=FormatSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), pathId);
595     m_pDS->exec(strSQL.c_str());
596
597     return true;
598   }
599   catch (...)
600   {
601     CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
602   }
603
604   return false;
605 }
606
607 bool CVideoDatabase::LinkMovieToTvshow(long idMovie, long idShow, bool bRemove)
608 {
609    try
610   {
611     if (NULL == m_pDB.get()) return false;
612     if (NULL == m_pDS.get()) return false;
613
614     if (bRemove) // delete link
615     {
616       CStdString strSQL=FormatSQL("delete from movielinktvshow where idMovie=%u and idShow=%u", idMovie, idShow);
617       m_pDS->exec(strSQL.c_str());
618       return true;
619     }
620
621     CStdString strSQL=FormatSQL("insert into movielinktvshow (idShow,idMovie) values (%u,%u)", idShow,idMovie);
622     m_pDS->exec(strSQL.c_str());
623
624     return true;
625   }
626   catch (...)
627   {
628     CLog::Log(LOGERROR, "%s (%ld, %ld) failed", __FUNCTION__, idMovie, idShow);
629   }
630
631   return false;
632 }
633
634 bool CVideoDatabase::IsLinkedToTvshow(long idMovie)
635 {
636    try
637   {
638     if (NULL == m_pDB.get()) return false;
639     if (NULL == m_pDS.get()) return false;
640
641     CStdString strSQL=FormatSQL("select * from movielinktvshow where idMovie=%u", idMovie);
642     m_pDS->query(strSQL.c_str());
643     if (m_pDS->eof())
644     {
645       m_pDS->close();
646       return false;
647     }
648
649     m_pDS->close();
650     return true;
651   }
652   catch (...)
653   {
654     CLog::Log(LOGERROR, "%s (%ld) failed", __FUNCTION__, idMovie);
655   }
656
657   return false;
658 }
659
660 bool CVideoDatabase::GetLinksToTvShow(long idMovie, vector<long>& ids)
661 {
662    try
663   {
664     if (NULL == m_pDB.get()) return false;
665     if (NULL == m_pDS.get()) return false;
666
667     CStdString strSQL=FormatSQL("select * from movielinktvshow where idMovie=%u", idMovie);
668     m_pDS->query(strSQL.c_str());
669     while (!m_pDS->eof())
670     {
671       ids.push_back(m_pDS->fv(1).get_asLong());
672       m_pDS->next();
673     }
674
675     m_pDS->close();
676     return true;
677   }
678   catch (...)
679   {
680     CLog::Log(LOGERROR, "%s (%ld) failed", __FUNCTION__, idMovie);
681   }
682
683   return false;
684 }
685
686
687 //********************************************************************************************************************************
688 long CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
689 {
690   try
691   {
692     if (NULL == m_pDB.get()) return -1;
693     if (NULL == m_pDS.get()) return -1;
694     CStdString strPath, strFileName;
695     SplitPath(strFilenameAndPath,strPath,strFileName);
696
697     long lPathId = GetPathId(strPath);
698     if (lPathId < 0)
699       return -1;
700
701     CStdString strSQL;
702     strSQL=FormatSQL("select idFile from files where strFileName like '%s' and idPath=%u", strFileName.c_str(),lPathId);
703     m_pDS->query(strSQL.c_str());
704     if (m_pDS->num_rows() > 0)
705     {
706       long lFileId = m_pDS->fv("files.idFile").get_asLong();
707       m_pDS->close();
708       return lFileId;
709     }
710   }
711   catch (...)
712   {
713     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
714   }
715   return -1;
716 }
717
718 //********************************************************************************************************************************
719 long CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
720 {
721   try
722   {
723     if (NULL == m_pDB.get()) return -1;
724     if (NULL == m_pDS.get()) return -1;
725     long lMovieId = -1;
726
727     // needed for query parameters
728     long lFileId = GetFileId(strFilenameAndPath);
729     long lPathId=-1;
730     CStdString strPath, strFile;
731     if (lFileId < 0)
732     {
733       SplitPath(strFilenameAndPath,strPath,strFile);
734
735       // have to join movieinfo table for correct results
736       lPathId = GetPathId(strPath);
737       if (lPathId < 0 && strPath != strFilenameAndPath)
738         return -1;
739     }
740
741     if (lFileId == -1 && strPath != strFilenameAndPath)
742       return -1;
743
744     CStdString strSQL;
745     if (lFileId == -1)
746       strSQL=FormatSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idpath = %u",lPathId);
747     else
748       strSQL=FormatSQL("select idMovie from movie where idFile=%u", lFileId);
749
750     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
751     m_pDS->query(strSQL.c_str());
752     if (m_pDS->num_rows() > 0)
753       lMovieId = m_pDS->fv("idMovie").get_asLong();
754     m_pDS->close();
755
756     return lMovieId;
757   }
758   catch (...)
759   {
760     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
761   }
762   return -1;
763 }
764
765 long CVideoDatabase::GetTvShowId(const CStdString& strPath)
766 {
767   try
768   {
769     if (NULL == m_pDB.get()) return -1;
770     if (NULL == m_pDS.get()) return -1;
771     long lTvShowId = -1;
772
773     // have to join movieinfo table for correct results
774     long lPathId = GetPathId(strPath);
775     if (lPathId < 0)
776       return -1;
777
778     CStdString strSQL;
779     CStdString strPath1=strPath;
780     CStdString strParent;
781     int iFound=0;
782
783     strSQL=FormatSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%u",lPathId);
784     m_pDS->query(strSQL);
785     if (!m_pDS->eof())
786       iFound = 1;
787
788     while (iFound == 0 && CUtil::GetParentPath(strPath1, strParent))
789     {
790       strSQL=FormatSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idpath = path.idpath and strPath like '%s'",strParent.c_str());
791       m_pDS->query(strSQL.c_str());
792       if (!m_pDS->eof())
793       {
794         long idShow = m_pDS->fv("idShow").get_asLong();
795         if (idShow != -1)
796           iFound = 2;
797       }
798       strPath1 = strParent;
799     }
800
801     if (m_pDS->num_rows() > 0)
802       lTvShowId = m_pDS->fv("idShow").get_asLong();
803     m_pDS->close();
804
805     return lTvShowId;
806   }
807   catch (...)
808   {
809     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
810   }
811   return -1;
812 }
813
814 long CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, long lEpisodeId, long lSeasonId) // input value is episode/season number hint - for multiparters
815 {
816   try
817   {
818     if (NULL == m_pDB.get()) return -1;
819     if (NULL == m_pDS.get()) return -1;
820
821     // need this due to the nested GetEpisodeInfo query
822     auto_ptr<Dataset> pDS;
823     pDS.reset(m_pDB->CreateDataset());
824     if (NULL == pDS.get()) return -1;
825
826     long lFileId = GetFileId(strFilenameAndPath);
827     if (lFileId < 0)
828       return -1;
829
830     CStdString strSQL=FormatSQL("select idEpisode from episode where idFile=%u", lFileId);
831
832     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
833     pDS->query(strSQL.c_str());
834     if (pDS->num_rows() > 0)
835     {
836       if (lEpisodeId == -1)
837         lEpisodeId = pDS->fv("episode.idEpisode").get_asLong();
838       else // use the hint!
839       {
840         while (!pDS->eof())
841         {
842           CVideoInfoTag tag;
843           long lTmpEpisodeId = pDS->fv("episode.idEpisode").get_asLong();
844           GetEpisodeInfo(strFilenameAndPath,tag,lTmpEpisodeId);
845           if (tag.m_iEpisode == lEpisodeId && (lSeasonId == -1 || tag.m_iSeason == lSeasonId)) {
846             // match on the episode hint, and there's no season hint or a season hint match
847             lEpisodeId = lTmpEpisodeId;
848             break;
849           }
850           pDS->next();
851         }
852         if (pDS->eof())
853           lEpisodeId = -1;
854       }
855     }
856     else
857       lEpisodeId = -1;
858
859     pDS->close();
860
861     return lEpisodeId;
862   }
863   catch (...)
864   {
865     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
866   }
867   return -1;
868 }
869
870 long CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
871 {
872   try
873   {
874     if (NULL == m_pDB.get()) return -1;
875     if (NULL == m_pDS.get()) return -1;
876
877     long lFileId = GetFileId(strFilenameAndPath);
878     if (lFileId < 0)
879       return -1;
880
881     CStdString strSQL=FormatSQL("select idMVideo from musicvideo where idFile=%u", lFileId);
882
883     CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
884     m_pDS->query(strSQL.c_str());
885     long lMVideoId=-1;
886     if (m_pDS->num_rows() > 0)
887       lMVideoId = m_pDS->fv("idMVideo").get_asLong();
888     m_pDS->close();
889
890     return lMVideoId;
891   }
892   catch (...)
893   {
894     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
895   }
896   return -1;
897 }
898
899 //********************************************************************************************************************************
900 long CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
901 {
902   try
903   {
904     if (NULL == m_pDB.get()) return -1;
905     if (NULL == m_pDS.get()) return -1;
906
907     long lFileId, lMovieId=-1;
908     lFileId = GetFileId(strFilenameAndPath);
909     if (lFileId < 0)
910       lFileId = AddFile(strFilenameAndPath);
911     else
912       lMovieId = GetMovieId(strFilenameAndPath);
913     if (lMovieId < 0)
914     {
915       CStdString strSQL=FormatSQL("insert into movie (idMovie, idFile) values (NULL, %u)", lFileId);
916       m_pDS->exec(strSQL.c_str());
917       lMovieId = (long)sqlite3_last_insert_rowid(m_pDB->getHandle());
918 //      CommitTransaction();
919     }
920
921     return lMovieId;
922   }
923   catch (...)
924   {
925     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
926   }
927   return -1;
928 }
929
930 long CVideoDatabase::AddTvShow(const CStdString& strPath)
931 {
932   try
933   {
934     if (NULL == m_pDB.get()) return -1;
935     if (NULL == m_pDS.get()) return -1;
936
937     CStdString strSQL=FormatSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath like '%s' and path.idPath = tvshowlinkpath.idPath",strPath.c_str());
938     m_pDS->query(strSQL.c_str());
939     if (m_pDS->num_rows() != 0)
940       return m_pDS->fv("tvshowlinkpath.idShow").get_asLong();
941
942     strSQL=FormatSQL("insert into tvshow (idShow) values (NULL)");
943     m_pDS->exec(strSQL.c_str());
944     long lTvShow = (long)sqlite3_last_insert_rowid(m_pDB->getHandle());
945
946     long lPathId = GetPathId(strPath);
947     if (lPathId < 0)
948       lPathId = AddPath(strPath);
949     strSQL=FormatSQL("insert into tvshowlinkpath values (%u,%u)",lTvShow,lPathId);
950     m_pDS->exec(strSQL.c_str());
951
952 //    CommitTransaction();
953
954     return lTvShow;
955   }
956   catch (...)
957   {
958     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
959   }
960   return -1;
961 }
962
963 //********************************************************************************************************************************
964 long CVideoDatabase::AddEpisode(long idShow, const CStdString& strFilenameAndPath)
965 {
966   try
967   {
968     if (NULL == m_pDB.get()) return -1;
969     if (NULL == m_pDS.get()) return -1;
970
971     long lFileId, lEpisodeId=-1;
972     lFileId = GetFileId(strFilenameAndPath);
973     if (lFileId < 0)
974       lFileId = AddFile(strFilenameAndPath);
975
976     CStdString strSQL=FormatSQL("insert into episode (idEpisode, idFile) values (NULL, %u)", lFileId);
977     m_pDS->exec(strSQL.c_str());
978     lEpisodeId = (long)sqlite3_last_insert_rowid(m_pDB->getHandle());
979
980     strSQL=FormatSQL("insert into tvshowlinkepisode (idShow,idEpisode) values (%i,%i)",idShow,lEpisodeId);
981     m_pDS->exec(strSQL.c_str());
982 //    CommitTransaction();
983
984     return lEpisodeId;
985   }
986   catch (...)
987   {
988     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
989   }
990   return -1;
991 }
992
993 long CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
994 {
995   try
996   {
997     if (NULL == m_pDB.get()) return -1;
998     if (NULL == m_pDS.get()) return -1;
999
1000     long lFileId, lMVideoId=-11;
1001     lFileId = GetFileId(strFilenameAndPath);
1002     if (lFileId < 0)
1003       lFileId = AddFile(strFilenameAndPath);
1004     else
1005       lMVideoId = GetMusicVideoId(strFilenameAndPath);
1006     if (lMVideoId < 0)
1007     {
1008       CStdString strSQL=FormatSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %u)", lFileId);
1009       m_pDS->exec(strSQL.c_str());
1010       lMVideoId = (long)sqlite3_last_insert_rowid(m_pDB->getHandle());
1011     }
1012
1013     return lMVideoId;
1014   }
1015   catch (...)
1016   {
1017     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1018   }
1019   return -1;
1020 }
1021
1022 //********************************************************************************************************************************
1023 long CVideoDatabase::AddGenre(const CStdString& strGenre)
1024 {
1025   try
1026   {
1027     if (NULL == m_pDB.get()) return -1;
1028     if (NULL == m_pDS.get()) return -1;
1029
1030     CStdString strSQL=FormatSQL("select idGenre from genre where strGenre like '%s'", strGenre.c_str());
1031     m_pDS->query(strSQL.c_str());
1032     if (m_pDS->num_rows() == 0)
1033     {
1034       m_pDS->close();
1035       // doesnt exists, add it
1036       strSQL=FormatSQL("insert into genre (idGenre, strGenre) values( NULL, '%s')", strGenre.c_str());
1037       m_pDS->exec(strSQL.c_str());
1038       long lGenreId = (long)sqlite3_last_insert_rowid(m_pDB->getHandle());
1039       return lGenreId;
1040     }
1041     else
1042     {
1043       const field_value value = m_pDS->fv("idGenre");
1044       long lGenreId = value.get_asLong() ;
1045       m_pDS->close();
1046       return lGenreId;
1047     }
1048   }
1049   catch (...)
1050   {
1051     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strGenre.c_str() );
1052   }
1053
1054   return -1;
1055 }
1056
1057 //********************************************************************************************************************************
1058 long CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& strThumb)
1059 {
1060   try
1061   {
1062     if (NULL == m_pDB.get()) return -1;
1063     if (NULL == m_pDS.get()) return -1;
1064     CStdString strSQL=FormatSQL("select idActor from Actors where strActor like '%s'", strActor.c_str());
1065     m_pDS->query(strSQL.c_str());
1066     if (m_pDS->num_rows() == 0)
1067     {
1068       m_pDS->close();
1069       // doesnt exists, add it
1070       strSQL=FormatSQL("insert into Actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),strThumb.c_str());
1071       m_pDS->exec(strSQL.c_str());
1072       long lActorId = (long)sqlite3_last_insert_rowid(m_pDB->getHandle());
1073       return lActorId;
1074     }
1075     else
1076     {
1077       const field_value value = m_pDS->fv("idActor");
1078       long lActorId = value.get_asLong() ;
1079       // update the thumb url's
1080       if (!strThumb.IsEmpty())
1081         strSQL=FormatSQL("update actors set strThumb='%s' where idActor=%u",strThumb.c_str(),lActorId);
1082       m_pDS->close();
1083       return lActorId;
1084     }
1085
1086   }
1087   catch (...)
1088   {
1089     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
1090   }
1091   return -1;
1092 }
1093
1094 long CVideoDatabase::AddStudio(const CStdString& strStudio)
1095 {
1096   try
1097   {
1098     if (NULL == m_pDB.get()) return -1;
1099     if (NULL == m_pDS.get()) return -1;
1100
1101     CStdString strSQL=FormatSQL("select idStudio from studio where strStudio like '%s'", strStudio.c_str());
1102     m_pDS->query(strSQL.c_str());
1103     if (m_pDS->num_rows() == 0)
1104     {
1105       m_pDS->close();
1106       // doesnt exists, add it
1107       strSQL=FormatSQL("insert into studio (idStudio, strStudio) values( NULL, '%s')", strStudio.c_str());
1108       m_pDS->exec(strSQL.c_str());
1109       long lStudioId = (long)sqlite3_last_insert_rowid(m_pDB->getHandle());
1110       return lStudioId;
1111     }
1112     else
1113     {
1114       const field_value value = m_pDS->fv("idStudio");
1115       long lStudioId = value.get_asLong() ;
1116       m_pDS->close();
1117       return lStudioId;
1118     }
1119   }
1120   catch (...)
1121   {
1122     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strStudio.c_str() );
1123   }
1124
1125   return -1;
1126 }
1127
1128 void CVideoDatabase::AddLinkToActor(const char *table, long actorID, const char *secondField, long secondID, const CStdString &role)
1129 {
1130   try
1131   {
1132     if (NULL == m_pDB.get()) return ;
1133     if (NULL == m_pDS.get()) return ;
1134
1135     CStdString strSQL=FormatSQL("select * from %s where idActor=%u and %s=%u", table, actorID, secondField, secondID);
1136     m_pDS->query(strSQL.c_str());
1137     if (m_pDS->num_rows() == 0)
1138     {
1139       // doesnt exists, add it
1140       strSQL=FormatSQL("insert into %s (idActor, %s, strRole) values(%i,%i,'%s')", table, secondField, actorID, secondID, role.c_str());
1141       m_pDS->exec(strSQL.c_str());
1142     }
1143     m_pDS->close();
1144   }
1145   catch (...)
1146   {
1147     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1148   }
1149 }
1150
1151 void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, long firstID, const char *secondField, long secondID)
1152 {
1153   try
1154   {
1155     if (NULL == m_pDB.get()) return ;
1156     if (NULL == m_pDS.get()) return ;
1157
1158     CStdString strSQL=FormatSQL("select * from %s where %s=%u and %s=%u", table, firstField, firstID, secondField, secondID);
1159     m_pDS->query(strSQL.c_str());
1160     if (m_pDS->num_rows() == 0)
1161     {
1162       // doesnt exists, add it
1163       strSQL=FormatSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
1164       m_pDS->exec(strSQL.c_str());
1165     }
1166     m_pDS->close();
1167   }
1168   catch (...)
1169   {
1170     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1171   }
1172 }
1173
1174 //****Actors****
1175 void CVideoDatabase::AddActorToMovie(long lMovieId, long lActorId, const CStdString& strRole)
1176 {
1177   AddLinkToActor("actorlinkmovie", lActorId, "idMovie", lMovieId, strRole);
1178 }
1179
1180 void CVideoDatabase::AddActorToTvShow(long lTvShowId, long lActorId, const CStdString& strRole)
1181 {
1182   AddLinkToActor("actorlinktvshow", lActorId, "idShow", lTvShowId, strRole);
1183 }
1184
1185 void CVideoDatabase::AddActorToEpisode(long lEpisodeId, long lActorId, const CStdString& strRole)
1186 {
1187   AddLinkToActor("actorlinkepisode", lActorId, "idEpisode", lEpisodeId, strRole);
1188 }
1189
1190 void CVideoDatabase::AddArtistToMusicVideo(long lMVideoId, long lArtistId)
1191 {
1192   AddToLinkTable("artistlinkmusicvideo", "idArtist", lArtistId, "idMVideo", lMVideoId);
1193 }
1194
1195 //****Directors + Writers****
1196 void CVideoDatabase::AddDirectorToMovie(long lMovieId, long lDirectorId)
1197 {
1198   AddToLinkTable("directorlinkmovie", "idDirector", lDirectorId, "idMovie", lMovieId);
1199 }
1200
1201 void CVideoDatabase::AddDirectorToTvShow(long lTvShowId, long lDirectorId)
1202 {
1203   AddToLinkTable("directorlinktvshow", "idDirector", lDirectorId, "idShow", lTvShowId);
1204 }
1205
1206 void CVideoDatabase::AddWriterToEpisode(long lEpisodeId, long lWriterId)
1207 {
1208   AddToLinkTable("writerlinkepisode", "idWriter", lWriterId, "idEpisode", lEpisodeId);
1209 }
1210
1211 void CVideoDatabase::AddWriterToMovie(long lMovieId, long lWriterId)
1212 {
1213   AddToLinkTable("writerlinkmovie", "idWriter", lWriterId, "idMovie", lMovieId);
1214 }
1215
1216 void CVideoDatabase::AddDirectorToEpisode(long lEpisodeId, long lDirectorId)
1217 {
1218   AddToLinkTable("directorlinkepisode", "idDirector", lDirectorId, "idEpisode", lEpisodeId);
1219 }
1220
1221 void CVideoDatabase::AddDirectorToMusicVideo(long lMVideoId, long lDirectorId)
1222 {
1223   AddToLinkTable("directorlinkmusicvideo", "idDirector", lDirectorId, "idMVideo", lMVideoId);
1224 }
1225
1226 //****Studios****
1227 void CVideoDatabase::AddStudioToMovie(long lMovieId, long lStudioId)
1228 {
1229   AddToLinkTable("studiolinkmovie", "idStudio", lStudioId, "idMovie", lMovieId);
1230 }
1231
1232 void CVideoDatabase::AddStudioToTvShow(long lTvShowId, long lStudioId)
1233 {
1234   AddToLinkTable("studiolinktvshow", "idStudio", lStudioId, "idShow", lTvShowId);
1235 }
1236
1237 void CVideoDatabase::AddStudioToMusicVideo(long lMVideoId, long lStudioId)
1238 {
1239   AddToLinkTable("studiolinkmusicvideo", "idStudio", lStudioId, "idMVideo", lMVideoId);
1240 }
1241
1242 //****Genres****
1243 void CVideoDatabase::AddGenreToMovie(long lMovieId, long lGenreId)
1244 {
1245   AddToLinkTable("genrelinkmovie", "idGenre", lGenreId, "idMovie", lMovieId);
1246 }
1247
1248 void CVideoDatabase::AddGenreToTvShow(long lTvShowId, long lGenreId)
1249 {
1250   AddToLinkTable("genrelinktvshow", "idGenre", lGenreId, "idShow", lTvShowId);
1251 }
1252
1253 void CVideoDatabase::AddGenreToMusicVideo(long lMVideoId, long lGenreId)
1254 {
1255   AddToLinkTable("genrelinkmusicvideo", "idGenre", lGenreId, "idMVideo", lMVideoId);
1256 }
1257
1258 //********************************************************************************************************************************
1259 bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
1260 {
1261   try
1262   {
1263     if (NULL == m_pDB.get()) return false;
1264     if (NULL == m_pDS.get()) return false;
1265     long lMovieId = GetMovieId(strFilenameAndPath);
1266     return (lMovieId > 0); // index of zero is also invalid
1267
1268     // work in progress
1269     if (lMovieId > 0)
1270     {
1271       // get title.  if no title, the id was "deleted" for in-place update
1272       CVideoInfoTag details;
1273       GetMovieInfo(strFilenameAndPath, details, lMovieId);
1274       if (!details.m_strTitle.IsEmpty()) return true;
1275     }
1276     return false;
1277   }
1278   catch (...)
1279   {
1280     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1281   }
1282   return false;
1283 }
1284
1285 bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
1286 {
1287   try
1288   {
1289     if (NULL == m_pDB.get()) return false;
1290     if (NULL == m_pDS.get()) return false;
1291     long lTvShowId = GetTvShowId(strPath);
1292     return (lTvShowId > 0); // index of zero is also invalid
1293
1294     // work in progress
1295     if (lTvShowId > 0)
1296     {
1297       // get title. if no title, the id was "deleted" for in-place update
1298       CVideoInfoTag details;
1299       GetTvShowInfo(strPath, details, lTvShowId);
1300       if (!details.m_strTitle.IsEmpty()) return true;
1301     }
1302     return false;
1303   }
1304   catch (...)
1305   {
1306     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1307   }
1308   return false;
1309 }
1310
1311 bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
1312 {
1313   try
1314   {
1315     if (NULL == m_pDB.get()) return false;
1316     if (NULL == m_pDS.get()) return false;
1317     long lEpisodeId = GetEpisodeId(strFilenameAndPath);
1318     return (lEpisodeId > 0); // index of zero is also invalid
1319
1320     // work in progress
1321     if (lEpisodeId > 0)
1322     {
1323       // get title.  if no title, the id was "deleted" for in-place update
1324       CVideoInfoTag details;
1325       GetEpisodeInfo(strFilenameAndPath, details, lEpisodeId);
1326       if (!details.m_strTitle.IsEmpty()) return true;
1327     }
1328     return false;
1329   }
1330   catch (...)
1331   {
1332     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1333   }
1334   return false;
1335 }
1336
1337 bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
1338 {
1339   try
1340   {
1341     if (NULL == m_pDB.get()) return false;
1342     if (NULL == m_pDS.get()) return false;
1343     long lMVideoId = GetMusicVideoId(strFilenameAndPath);
1344     return (lMVideoId > 0); // index of zero is also invalid
1345
1346     // work in progress
1347     if (lMVideoId > 0)
1348     {
1349       // get title.  if no title, the id was "deleted" for in-place update
1350       CVideoInfoTag details;
1351       GetMusicVideoInfo(strFilenameAndPath, details, lMVideoId);
1352       if (!details.m_strTitle.IsEmpty()) return true;
1353     }
1354     return false;
1355   }
1356   catch (...)
1357   {
1358     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1359   }
1360   return false;
1361 }
1362
1363 void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath)
1364 {// TODO: merge into DeleteTvShow
1365   try
1366   {
1367     if (NULL == m_pDB.get()) return ;
1368     if (NULL == m_pDS.get()) return ;
1369
1370     long lTvShowId = GetTvShowId(strPath);
1371     if ( lTvShowId < 0) return ;
1372
1373     CFileItemList items;
1374     CStdString strPath2;
1375     strPath2.Format("videodb://2/2/%u/",lTvShowId);
1376     GetSeasonsNav(strPath2,items,-1,-1,-1,-1,lTvShowId);
1377     for( int i=0;i<items.Size();++i )
1378       XFILE::CFile::Delete(items[i]->GetCachedSeasonThumb());
1379
1380     DeleteThumbForItem(strPath,true);
1381
1382     CStdString strSQL;
1383     strSQL=FormatSQL("delete from genrelinktvshow where idshow=%i", lTvShowId);
1384     m_pDS->exec(strSQL.c_str());
1385
1386     strSQL=FormatSQL("delete from actorlinktvshow where idshow=%i", lTvShowId);
1387     m_pDS->exec(strSQL.c_str());
1388
1389     strSQL=FormatSQL("delete from directorlinktvshow where idshow=%i", lTvShowId);
1390     m_pDS->exec(strSQL.c_str());
1391
1392     strSQL=FormatSQL("delete from studiolinktvshow where idshow=%i", lTvShowId);
1393     m_pDS->exec(strSQL.c_str());
1394
1395     // remove all info other than the id
1396     // we do this due to the way we have the link between the file + movie tables.
1397
1398     strSQL = "update tvshow set ";
1399     for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1400     {
1401       CStdString column;
1402       column.Format("c%02d=NULL,", iType);
1403       strSQL += column;
1404     }
1405     strSQL = strSQL.Mid(0, strSQL.size() - 1) + FormatSQL(" where idShow=%i", lTvShowId);
1406     m_pDS->exec(strSQL.c_str());
1407   }
1408   catch (...)
1409   {
1410     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1411   }
1412 }
1413
1414 //********************************************************************************************************************************
1415 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, VECMOVIES& movies)
1416 {
1417   try
1418   {
1419     movies.erase(movies.begin(), movies.end());
1420     if (NULL == m_pDB.get()) return ;
1421     if (NULL == m_pDS.get()) return ;
1422
1423     CStdString strSQL=FormatSQL("select * from movieview join actorlinkmovie on actorlinkmovie.idmovie=movieview.idmovie join actors on actors.idActor=actorlinkmovie.idActor where actors.stractor='%s'", strActor.c_str());
1424     m_pDS->query( strSQL.c_str() );
1425
1426     while (!m_pDS->eof())
1427     {
1428       movies.push_back(GetDetailsForMovie(m_pDS));
1429       m_pDS->next();
1430     }
1431     m_pDS->close();
1432   }
1433   catch (...)
1434   {
1435     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str());
1436   }
1437 }
1438
1439 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, VECMOVIES& movies)
1440 {
1441   try
1442   {
1443     movies.erase(movies.begin(), movies.end());
1444     if (NULL == m_pDB.get()) return ;
1445     if (NULL == m_pDS.get()) return ;
1446
1447     CStdString strSQL = FormatSQL("select * from tvshowview join actorlinktvshow on actorlinktvshow.idshow=tvshowview.idshow "
1448                                   "join actors on actors.idActor=actorlinktvshow.idActor "
1449                                   "where actors.stractor='%s'", strActor.c_str());
1450
1451     m_pDS->query( strSQL.c_str() );
1452
1453     while (!m_pDS->eof())
1454     {
1455       movies.push_back(GetDetailsForTvShow(m_pDS));
1456       m_pDS->next();
1457     }
1458     m_pDS->close();
1459   }
1460   catch (...)
1461   {
1462     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str());
1463   }
1464 }
1465
1466 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, VECMOVIES& movies)
1467 {
1468   try
1469   {
1470     movies.erase(movies.begin(), movies.end());
1471     if (NULL == m_pDB.get()) return ;
1472     if (NULL == m_pDS.get()) return ;
1473
1474     CStdString strSQL=FormatSQL("select * from episodeview join actorlinkepisode on actorlinkepisode.idepisode=episodeview.idepisode join actors on actors.idActor=actorlinkepisode.idActor where actors.stractor='%s'", strActor.c_str());
1475     m_pDS->query( strSQL.c_str() );
1476
1477     while (!m_pDS->eof())
1478     {
1479       CVideoInfoTag movie=GetDetailsForEpisode(m_pDS);
1480       movie.m_strTitle += " ("+m_pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString()+")";
1481       movies.push_back(movie);
1482       m_pDS->next();
1483     }
1484     m_pDS->close();
1485   }
1486   catch (...)
1487   {
1488     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str());
1489   }
1490 }
1491
1492 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1493 {
1494   try
1495   {
1496     items.Clear();
1497     if (NULL == m_pDB.get()) return ;
1498     if (NULL == m_pDS.get()) return ;
1499
1500     CStdString strSQL;
1501     if (strArtist.IsEmpty())  // TODO: SMARTPLAYLISTS what is this here for???
1502       strSQL=FormatSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idmvideo=musicvideoview.idmvideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1503     else
1504       strSQL=FormatSQL("select * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idmvideo=musicvideoview.idmvideo join actors on actors.idActor=artistlinkmusicvideo.idArtist where actors.stractor='%s'", strArtist.c_str());
1505     m_pDS->query( strSQL.c_str() );
1506
1507     while (!m_pDS->eof())
1508     {
1509       CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1510       CFileItemPtr pItem(new CFileItem(tag));
1511       pItem->SetLabel(tag.m_strArtist);
1512       items.Add(pItem);
1513       m_pDS->next();
1514     }
1515     m_pDS->close();
1516   }
1517   catch (...)
1518   {
1519     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1520   }
1521 }
1522
1523 //********************************************************************************************************************************
1524 void CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, long lMovieId /* = -1 */)
1525 {
1526   try
1527   {
1528     // TODO: Optimize this - no need for all the queries!
1529     if (lMovieId < 0)
1530       lMovieId = GetMovieId(strFilenameAndPath);
1531     if (lMovieId < 0) return ;
1532
1533     CStdString sql = FormatSQL("select * from movieview where idMovie=%i", lMovieId);
1534     if (!m_pDS->query(sql.c_str()))
1535       return;
1536     details = GetDetailsForMovie(m_pDS, true);
1537   }
1538   catch (...)
1539   {
1540     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1541   }
1542 }
1543
1544 //********************************************************************************************************************************
1545 void CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, long lTvShowId /* = -1 */)
1546 {
1547   try
1548   {
1549     if (lTvShowId < 0)
1550       lTvShowId = GetTvShowId(strPath);
1551     if (lTvShowId < 0) return ;
1552
1553     CStdString sql = FormatSQL("select * from tvshowview where idshow=%i", lTvShowId);
1554     if (!m_pDS->query(sql.c_str()))
1555       return;
1556     details = GetDetailsForTvShow(m_pDS, true);
1557   }
1558   catch (...)
1559   {
1560     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1561   }
1562 }
1563
1564 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, long lEpisodeId /* = -1 */)
1565 {
1566   try
1567   {
1568     // TODO: Optimize this - no need for all the queries!
1569     if (lEpisodeId < 0)
1570       lEpisodeId = GetEpisodeId(strFilenameAndPath);
1571     if (lEpisodeId < 0) return false;
1572
1573     CStdString sql = FormatSQL("select * from episodeview where idepisode=%u",lEpisodeId);
1574     if (!m_pDS->query(sql.c_str()))
1575       return false;
1576     details = GetDetailsForEpisode(m_pDS, true);
1577     return true;
1578   }
1579   catch (...)
1580   {
1581     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1582   }
1583   return false;
1584 }
1585
1586 void CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, long lMVideoId /* = -1 */)
1587 {
1588   try
1589   {
1590     // TODO: Optimize this - no need for all the queries!
1591     if (lMVideoId < 0)
1592       lMVideoId = GetMusicVideoId(strFilenameAndPath);
1593     if (lMVideoId < 0) return ;
1594
1595     CStdString sql = FormatSQL("select * from musicvideoview where idmvideo=%i", lMVideoId);
1596     if (!m_pDS->query(sql.c_str()))
1597       return;
1598     details = GetDetailsForMusicVideo(m_pDS);
1599   }
1600   catch (...)
1601   {
1602     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1603   }
1604 }
1605
1606 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<long>& vecDirectors, vector<long>& vecGenres, vector<long>& vecStudios)
1607 {
1608   // add all directors
1609   if (!details.m_strDirector.IsEmpty())
1610   {
1611     CStdStringArray directors;
1612     StringUtils::SplitString(details.m_strDirector, "/", directors);
1613     for (unsigned int i = 0; i < directors.size(); i++)
1614     {
1615       CStdString strDirector(directors[i]);
1616       strDirector.Trim();
1617       long lDirectorId = AddActor(strDirector,"");
1618       vecDirectors.push_back(lDirectorId);
1619     }
1620   }
1621
1622   // add all genres
1623   if (!details.m_strGenre.IsEmpty())
1624   {
1625     CStdStringArray genres;
1626     StringUtils::SplitString(details.m_strGenre, "/", genres);
1627     for (unsigned int i = 0; i < genres.size(); i++)
1628     {
1629       CStdString strGenre(genres[i]);
1630       strGenre.Trim();
1631       long lGenreId = AddGenre(strGenre);
1632       vecGenres.push_back(lGenreId);
1633     }
1634   }
1635   // add all studios
1636   if (!details.m_strStudio.IsEmpty())
1637   {
1638     CStdStringArray studios;
1639     StringUtils::SplitString(details.m_strStudio, "/", studios);
1640     for (unsigned int i = 0; i < studios.size(); i++)
1641     {
1642       CStdString strStudio(studios[i]);
1643       strStudio.Trim();
1644       long lStudioId = AddStudio(strStudio);
1645       vecStudios.push_back(lStudioId);
1646     }
1647   }
1648 }
1649
1650 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1651 {
1652   CStdString sql;
1653   for (int i = min + 1; i < max; ++i)
1654   {
1655     switch (offsets[i].type)
1656     {
1657     case VIDEODB_TYPE_STRING:
1658       sql += FormatSQL("c%02d='%s',", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str());
1659       break;
1660     case VIDEODB_TYPE_INT:
1661       sql += FormatSQL("c%02d='%i',", i, *(int*)(((char*)&details)+offsets[i].offset));
1662       break;
1663     case VIDEODB_TYPE_COUNT:
1664       {
1665         int value = *(int*)(((char*)&details)+offsets[i].offset);
1666         if (value)
1667           sql += FormatSQL("c%02d=%i,", i, value);
1668         else
1669           sql += FormatSQL("c%02d=NULL,", i);
1670       }
1671       break;
1672     case VIDEODB_TYPE_BOOL:
1673       sql += FormatSQL("c%02d='%s',", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false");
1674       break;
1675     case VIDEODB_TYPE_FLOAT:
1676       sql += FormatSQL("c%02d='%f',", i, *(float*)(((char*)&details)+offsets[i].offset));
1677       break;
1678     }
1679   }
1680   sql.TrimRight(',');
1681   return sql;
1682 }
1683
1684 //********************************************************************************************************************************
1685 void CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details)
1686 {
1687   try
1688   {
1689     CVideoInfoTag info = details;
1690
1691     long lFileId = GetFileId(strFilenameAndPath);
1692     
1693     if (lFileId < 0)
1694       lFileId = AddFile(strFilenameAndPath);
1695     
1696     long lMovieId = GetMovieId(strFilenameAndPath);
1697     
1698     if (lMovieId > -1)
1699       DeleteMovie(strFilenameAndPath, true); // true to keep the table entry
1700     
1701     BeginTransaction();
1702     
1703     lMovieId = AddMovie(strFilenameAndPath);
1704     if (lMovieId < 0)
1705     {
1706       CommitTransaction();
1707       return;
1708     }
1709
1710     vector<long> vecDirectors;
1711     vector<long> vecGenres;
1712     vector<long> vecStudios;
1713     AddGenreAndDirectorsAndStudios(info,vecDirectors,vecGenres,vecStudios);
1714
1715     for (unsigned int i = 0; i < vecGenres.size(); ++i)
1716       AddGenreToMovie(lMovieId, vecGenres[i]);
1717
1718     for (unsigned int i = 0; i < vecDirectors.size(); ++i)
1719       AddDirectorToMovie(lMovieId, vecDirectors[i]);
1720
1721     for (unsigned int i = 0; i < vecStudios.size(); ++i)
1722       AddStudioToMovie(lMovieId, vecStudios[i]);
1723
1724     // add writers...
1725     if (!info.m_strWritingCredits.IsEmpty())
1726     {
1727       CStdStringArray writers;
1728       StringUtils::SplitString(info.m_strWritingCredits, "/", writers);
1729       for (unsigned int i = 0; i < writers.size(); i++)
1730       {
1731         CStdString writer(writers[i]);
1732         writer.Trim();
1733         long lWriterId = AddActor(writer,"");
1734         AddWriterToMovie(lMovieId, lWriterId );
1735       }
1736     }
1737
1738     // add cast...
1739     for (CVideoInfoTag::iCast it = info.m_cast.begin(); it != info.m_cast.end(); ++it)
1740     {
1741       long lActor = AddActor(it->strName,it->thumbUrl.m_xml);
1742       AddActorToMovie(lMovieId, lActor, it->strRole);
1743     }
1744
1745     if (details.HasStreamDetails())
1746       SetStreamDetailsForFileId(details.m_streamDetails, lFileId);
1747
1748     // update our movie table (we know it was added already above)
1749     // and insert the new row
1750     CStdString sql = "update movie set " + GetValueString(info, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
1751     sql += FormatSQL(" where idMovie=%u", lMovieId);
1752     m_pDS->exec(sql.c_str());
1753     CommitTransaction();
1754   }
1755   catch (...)
1756   {
1757     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1758   }
1759 }
1760
1761 long CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details)
1762 {
1763   try
1764   {
1765     if (!m_pDB.get() || !m_pDS.get())
1766     {
1767       CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
1768       return -1;
1769     }
1770     
1771     BeginTransaction();
1772     
1773     long lTvShowId = GetTvShowId(strPath);
1774     if (lTvShowId < 0)
1775       lTvShowId = AddTvShow(strPath);
1776
1777     vector<long> vecDirectors;
1778     vector<long> vecGenres;
1779     vector<long> vecStudios;
1780     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1781
1782     // add cast...
1783     for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
1784     {
1785       long lActor = AddActor(it->strName,it->thumbUrl.m_xml);
1786       AddActorToTvShow(lTvShowId, lActor, it->strRole);
1787     }
1788
1789     unsigned int i;
1790     for (i = 0; i < vecGenres.size(); ++i)
1791     {
1792       AddGenreToTvShow(lTvShowId, vecGenres[i]);
1793     }
1794
1795     for (i = 0; i < vecDirectors.size(); ++i)
1796     {
1797       AddDirectorToTvShow(lTvShowId, vecDirectors[i]);
1798     }
1799
1800     for (i = 0; i < vecStudios.size(); ++i)
1801     {
1802       AddStudioToTvShow(lTvShowId, vecStudios[i]);
1803     }
1804
1805     // and insert the new row
1806     CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
1807     sql += FormatSQL("where idShow=%u", lTvShowId);
1808     m_pDS->exec(sql.c_str());
1809     CommitTransaction();
1810     return lTvShowId;
1811   }
1812   catch (...)
1813   {
1814     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1815   }
1816
1817   return -1;
1818 }
1819
1820 long CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, long idShow, long lEpisodeId)
1821 {
1822   try
1823   {
1824     BeginTransaction();
1825     if (lEpisodeId == -1)
1826     {
1827       lEpisodeId = GetEpisodeId(strFilenameAndPath);
1828       if (lEpisodeId > 0)
1829         DeleteEpisode(strFilenameAndPath,lEpisodeId);
1830
1831       lEpisodeId = AddEpisode(idShow,strFilenameAndPath);
1832       if (lEpisodeId < 0)
1833       {
1834         CommitTransaction();
1835         return -1;
1836       }
1837     }
1838
1839     vector<long> vecDirectors;
1840     vector<long> vecGenres;
1841     vector<long> vecStudios;
1842     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1843
1844     // add cast...
1845     for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
1846     {
1847       long lActor = AddActor(it->strName,it->thumbUrl.m_xml);
1848       AddActorToEpisode(lEpisodeId, lActor, it->strRole);
1849     }
1850
1851     // add writers...
1852     if (!details.m_strWritingCredits.IsEmpty())
1853     {
1854       CStdStringArray writers;
1855       StringUtils::SplitString(details.m_strWritingCredits, "/", writers);
1856       for (unsigned int i = 0; i < writers.size(); i++)
1857       {
1858         CStdString writer(writers[i]);
1859         writer.Trim();
1860         long lWriterId = AddActor(writer,"");
1861         AddWriterToEpisode(lEpisodeId, lWriterId );
1862       }
1863     }
1864
1865     for (unsigned int i = 0; i < vecDirectors.size(); ++i)
1866     {
1867       AddDirectorToEpisode(lEpisodeId, vecDirectors[i]);
1868     }
1869
1870     if (details.HasStreamDetails())
1871     {
1872       if (details.m_iFileId != -1)
1873         SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
1874       else
1875         SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
1876     }
1877
1878     // and insert the new row
1879     CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
1880     sql += FormatSQL("where idEpisode=%u", lEpisodeId);
1881     m_pDS->exec(sql.c_str());
1882     CommitTransaction();
1883     return lEpisodeId;
1884   }
1885   catch (...)
1886   {
1887     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1888   }
1889   return -1;
1890 }
1891
1892 void CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details)
1893 {
1894   try
1895   {
1896     BeginTransaction();
1897     
1898     long lFileId = GetFileId(strFilenameAndPath);
1899     if (lFileId < 0)
1900       lFileId = AddFile(strFilenameAndPath);
1901     long lMVideoId = GetMusicVideoId(strFilenameAndPath);
1902     if (lMVideoId > -1)
1903     {
1904       DeleteMusicVideo(strFilenameAndPath);
1905     }
1906     lMVideoId = AddMusicVideo(strFilenameAndPath);
1907     if (lMVideoId < 0)
1908     {
1909       CommitTransaction();
1910       return;
1911     }
1912
1913     vector<long> vecDirectors;
1914     vector<long> vecGenres;
1915     vector<long> vecStudios;
1916     AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1917
1918     // add artists...
1919     if (!details.m_strArtist.IsEmpty())
1920     {
1921       CStdStringArray vecArtists;
1922       StringUtils::SplitString(details.m_strArtist, g_advancedSettings.m_videoItemSeparator, vecArtists);
1923       for (unsigned int i = 0; i < vecArtists.size(); i++)
1924       {
1925         CStdString artist = vecArtists[i];
1926         artist.Trim();
1927         long lArtist = AddActor(artist,"");
1928         AddArtistToMusicVideo(lMVideoId, lArtist);
1929       }
1930     }
1931
1932     unsigned int i;
1933     for (i = 0; i < vecGenres.size(); ++i)
1934     {
1935       AddGenreToMusicVideo(lMVideoId, vecGenres[i]);
1936     }
1937
1938     for (i = 0; i < vecDirectors.size(); ++i)
1939     {
1940       AddDirectorToMusicVideo(lMVideoId, vecDirectors[i]);
1941     }
1942
1943     for (i = 0; i < vecStudios.size(); ++i)
1944     {
1945       AddStudioToMusicVideo(lMVideoId, vecStudios[i]);
1946     }
1947
1948     if (details.HasStreamDetails())
1949       SetStreamDetailsForFileId(details.m_streamDetails, lFileId);
1950
1951     // update our movie table (we know it was added already above)
1952     // and insert the new row
1953     CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
1954     sql += FormatSQL(" where idMVideo=%u", lMVideoId);
1955     m_pDS->exec(sql.c_str());
1956     CommitTransaction();
1957   }
1958   catch (...)
1959   {
1960     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1961   }
1962 }
1963
1964 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
1965 {
1966   // AddFile checks to make sure the file isn't already in the DB first
1967   long lFileId = AddFile(strFileNameAndPath);
1968   if (lFileId < 0) 
1969     return;
1970   SetStreamDetailsForFileId(details, lFileId);
1971 }
1972
1973 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, long lFileId)
1974 {
1975   if (lFileId < 0)
1976     return;
1977
1978   try
1979   {
1980     BeginTransaction();
1981     m_pDS->exec(FormatSQL("DELETE FROM streamdetails WHERE idFile = %u", lFileId));
1982
1983     for (int i=1; i<=details.GetVideoStreamCount(); i++)
1984     {
1985       m_pDS->exec(FormatSQL("INSERT INTO streamdetails "
1986         "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight) "
1987         "VALUES (%u,%i,'%s',%f,%i,%i)",
1988         lFileId, (int)CStreamDetail::VIDEO,
1989         details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
1990         details.GetVideoWidth(i), details.GetVideoHeight(i)));
1991     }
1992     for (int i=1; i<=details.GetAudioStreamCount(); i++)
1993     {
1994       m_pDS->exec(FormatSQL("INSERT INTO streamdetails "
1995         "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
1996         "VALUES (%u,%i,'%s',%i,'%s')",
1997         lFileId, (int)CStreamDetail::AUDIO,
1998         details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
1999         details.GetAudioLanguage(i).c_str()));
2000     }
2001     for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2002     {
2003       m_pDS->exec(FormatSQL("INSERT INTO streamdetails "
2004         "(idFile, iStreamType, strSubtitleLanguage) "
2005         "VALUES (%u,%i,'%s')",
2006         lFileId, (int)CStreamDetail::SUBTITLE,
2007         details.GetSubtitleLanguage(i).c_str()));
2008     }
2009
2010     CommitTransaction();
2011   }
2012   catch (...)
2013   {
2014     RollbackTransaction();
2015     CLog::Log(LOGERROR, "%s (%lu) failed", __FUNCTION__, lFileId);
2016   }
2017 }
2018
2019 //********************************************************************************************************************************
2020 void CVideoDatabase::GetFilePathById(long lMovieId, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2021 {
2022   try
2023   {
2024     if (NULL == m_pDB.get()) return ;
2025     if (NULL == m_pDS.get()) return ;
2026
2027     if (lMovieId < 0) return ;
2028
2029     CStdString strSQL;
2030     if (iType == VIDEODB_CONTENT_MOVIES)
2031       strSQL=FormatSQL("select path.strPath,files.strFileName from path, files, movie where path.idPath=files.idPath and files.idFile=movie.idFile and movie.idMovie=%u order by strFilename", lMovieId );
2032     if (iType == VIDEODB_CONTENT_EPISODES)
2033       strSQL=FormatSQL("select path.strPath,files.strFileName from path, files, episode where path.idPath=files.idPath and files.idFile=episode.idFile and episode.idEpisode=%u order by strFilename", lMovieId );
2034     if (iType == VIDEODB_CONTENT_TVSHOWS)
2035       strSQL=FormatSQL("select path.strPath from path,tvshowlinkpath where path.idpath=tvshowlinkpath.idpath and tvshowlinkpath.idshow=%i", lMovieId );
2036     if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2037       strSQL=FormatSQL("select path.strPath,files.strFileName from path, files, musicvideo where path.idPath=files.idPath and files.idFile=musicvideo.idFile and musicvideo.idmvideo=%u order by strFilename", lMovieId );
2038
2039     m_pDS->query( strSQL.c_str() );
2040     if (!m_pDS->eof())
2041     {
2042       if (iType != VIDEODB_CONTENT_TVSHOWS)
2043       {
2044         CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2045         ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2046       }
2047       else
2048         filePath = m_pDS->fv("path.strPath").get_asString();
2049     }
2050     m_pDS->close();
2051   }
2052   catch (...)
2053   {
2054     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2055   }
2056 }
2057
2058 //********************************************************************************************************************************
2059 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend)
2060 {
2061   try
2062   {
2063     long lFileId = GetFileId(strFilenameAndPath);
2064     if (lFileId < 0) return ;
2065     if (!bAppend)
2066       bookmarks.erase(bookmarks.begin(), bookmarks.end());
2067     if (NULL == m_pDB.get()) return ;
2068     if (NULL == m_pDS.get()) return ;
2069
2070     CStdString strSQL=FormatSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", lFileId, (int)type);
2071     m_pDS->query( strSQL.c_str() );
2072     while (!m_pDS->eof())
2073     {
2074       CBookmark bookmark;
2075       bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2076       bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2077       bookmark.playerState = m_pDS->fv("playerState").get_asString();
2078       bookmark.player = m_pDS->fv("player").get_asString();
2079       bookmark.type = type;
2080       if (type == CBookmark::EPISODE)
2081       {
2082         CStdString strSQL2=FormatSQL("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_asInteger(), VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTEPISODE);
2083         m_pDS2->query(strSQL2.c_str());
2084         bookmark.episodeNumber = m_pDS2->fv(0).get_asLong();
2085         bookmark.seasonNumber = m_pDS2->fv(1).get_asLong();
2086         m_pDS2->close();
2087       }
2088       bookmarks.push_back(bookmark);
2089       m_pDS->next();
2090     }
2091     //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2092     m_pDS->close();
2093   }
2094   catch (...)
2095   {
2096     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2097   }
2098 }
2099
2100 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2101 {
2102   VECBOOKMARKS bookmarks;
2103   GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2104   if (bookmarks.size() > 0)
2105   {
2106     bookmark = bookmarks[0];
2107     return true;
2108   }
2109   return false;
2110 }
2111
2112 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2113 {
2114   if (!m_pDB.get() || !m_pDS.get())
2115     return;
2116
2117   int fileID = GetFileId(strFilenameAndPath);
2118   if (fileID < -1)
2119     return;
2120
2121   try
2122   {
2123     CStdString sql = FormatSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2124     m_pDS->exec(sql.c_str());
2125   }
2126   catch(...)
2127   {
2128     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2129   }
2130 }
2131
2132 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2133 {
2134   try
2135   {
2136     CStdString strSQL = FormatSQL("select * from episodeview where idFile=%i order by c%02d, c%02d asc", GetFileId(strFilenameAndPath), VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTEPISODE);
2137     m_pDS->query(strSQL.c_str());
2138     while (!m_pDS->eof())
2139     {
2140       episodes.push_back(GetDetailsForEpisode(m_pDS));
2141       m_pDS->next();
2142     }
2143     m_pDS->close();
2144   }
2145   catch (...)
2146   {
2147     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2148   }
2149 }
2150
2151 //********************************************************************************************************************************
2152 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2153 {
2154   try
2155   {
2156     long lFileId = GetFileId(strFilenameAndPath);
2157     if (lFileId < 0)
2158     {
2159       // Doesn't exist in the database yet - add it.
2160       // TODO: It doesn't appear to me that the CDLabel parameter or the subtitles
2161       // parameter is anywhere in use in XBMC.
2162       lFileId = AddFile(strFilenameAndPath);
2163       if (lFileId < 0)
2164         return;
2165     }
2166     if (NULL == m_pDB.get()) return ;
2167     if (NULL == m_pDS.get()) return ;
2168
2169     CStdString strSQL;
2170     int idBookmark=-1;
2171     if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2172     {
2173       strSQL=FormatSQL("select idBookmark from bookmark where idFile=%i and type=1", lFileId);
2174     }
2175     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
2176     {
2177       /* get a bookmark within the same time as previous */
2178       double mintime = bookmark.timeInSeconds - 0.5f;
2179       double maxtime = bookmark.timeInSeconds + 0.5f;
2180       strSQL=FormatSQL("select idBookmark from bookmark where idFile=%i and type=%i and (timeInSeconds between %f and %f) and playerState='%s'", lFileId, (int)type, mintime, maxtime, bookmark.playerState.c_str());
2181     }
2182
2183     if (type != CBookmark::EPISODE)
2184     {
2185       // get current id
2186       m_pDS->query( strSQL.c_str() );
2187       if (m_pDS->num_rows() != 0)
2188         idBookmark = m_pDS->get_field_value("idBookmark").get_asInteger();
2189       m_pDS->close();
2190     }
2191     // update or insert depending if it existed before
2192     if (idBookmark >= 0 )
2193       strSQL=FormatSQL("update bookmark set timeInSeconds = %f, thumbNailImage = '%s', player = '%s', playerState = '%s' where idBookmark = %i", bookmark.timeInSeconds, bookmark.thumbNailImage.c_str(), bookmark.player.c_str(), bookmark.playerState.c_str(), idBookmark);
2194     else
2195       strSQL=FormatSQL("insert into bookmark (idBookmark, idFile, timeInSeconds, thumbNailImage, player, playerState, type) values(NULL,%i,%f,'%s','%s','%s', %i)", lFileId, bookmark.timeInSeconds, bookmark.thumbNailImage.c_str(), bookmark.player.c_str(), bookmark.playerState.c_str(), (int)type);
2196     
2197     m_pDS->exec(strSQL.c_str());
2198   }
2199   catch (...)
2200   {
2201     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2202   }
2203 }
2204
2205 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2206 {
2207   try
2208   {
2209     long lFileId = GetFileId(strFilenameAndPath);
2210     if (lFileId < 0) return ;
2211     if (NULL == m_pDB.get()) return ;
2212     if (NULL == m_pDS.get()) return ;
2213
2214     /* a litle bit uggly, we clear first bookmark that is within one second of given */
2215     /* should be no problem since we never add bookmarks that are closer than that   */
2216     double mintime = bookmark.timeInSeconds - 0.5f;
2217     double maxtime = bookmark.timeInSeconds + 0.5f;
2218     CStdString strSQL = FormatSQL("select idBookmark from bookmark where idFile=%i and type=%i and playerState like '%s' and player like '%s' and (timeInSeconds between %f and %f)", lFileId, type, bookmark.playerState.c_str(), bookmark.player.c_str(), mintime, maxtime);
2219
2220     m_pDS->query( strSQL.c_str() );
2221     if (m_pDS->num_rows() != 0)
2222     {
2223       int idBookmark = m_pDS->get_field_value("idBookmark").get_asInteger();
2224       strSQL=FormatSQL("delete from bookmark where idBookmark=%i",idBookmark);
2225       m_pDS->exec(strSQL.c_str());
2226       if (type == CBookmark::EPISODE)
2227       {
2228         strSQL=FormatSQL("update episode set c%02d=-1 where idFile=%i and c%02d=%i", VIDEODB_ID_EPISODE_BOOKMARK, lFileId, VIDEODB_ID_EPISODE_BOOKMARK, idBookmark);
2229         m_pDS->exec(strSQL.c_str());
2230       }
2231     }
2232
2233     m_pDS->close();
2234   }
2235   catch (...)
2236   {
2237     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2238   }
2239 }
2240
2241 //********************************************************************************************************************************
2242 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2243 {
2244   try
2245   {
2246     long lFileId = GetFileId(strFilenameAndPath);
2247     if (lFileId < 0) return ;
2248     if (NULL == m_pDB.get()) return ;
2249     if (NULL == m_pDS.get()) return ;
2250
2251     CStdString strSQL=FormatSQL("delete from bookmark where idFile=%i and type=%i", lFileId, (int)type);
2252     m_pDS->exec(strSQL.c_str());
2253     if (type == CBookmark::EPISODE)
2254     {
2255       strSQL=FormatSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, lFileId);
2256       m_pDS->exec(strSQL.c_str());
2257     }
2258   }
2259   catch (...)
2260   {
2261     CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2262   }
2263 }
2264
2265
2266 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2267 {
2268   try
2269   {
2270     CStdString strSQL = FormatSQL("select bookmark.* from bookmark join episode on episode.c%02d=bookmark.idBookmark where episode.idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2271     m_pDS->query( strSQL.c_str() );
2272     if (!m_pDS->eof())
2273     {
2274       bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2275       bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2276       bookmark.playerState = m_pDS->fv("playerState").get_asString();
2277       bookmark.player = m_pDS->fv("player").get_asString();
2278       bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInteger();
2279     }
2280     else
2281     {
2282       m_pDS->close();
2283       return false;
2284     }
2285     m_pDS->close();
2286   }
2287   catch (...)
2288   {
2289     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2290     return false;
2291   }
2292   return true;
2293 }
2294
2295 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2296 {
2297   try
2298   {
2299     long lFileId = GetFileId(tag.m_strFileNameAndPath);
2300     // delete the current episode for the selected episode number
2301     CStdString strSQL = FormatSQL("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, lFileId);
2302     m_pDS->exec(strSQL.c_str());
2303
2304     AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2305     long lBookmarkId = (long)sqlite3_last_insert_rowid(m_pDB->getHandle());
2306     strSQL = FormatSQL("update episode set c%02d=%i where c%02d=%i and c%02d=%i and idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, lBookmarkId, VIDEODB_ID_EPISODE_SEASON, tag.m_iSeason, VIDEODB_ID_EPISODE_EPISODE, tag.m_iEpisode, lFileId);
2307     m_pDS->exec(strSQL.c_str());
2308   }
2309   catch (...)
2310   {
2311     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2312   }
2313 }
2314
2315 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2316 {
2317   try
2318   {
2319     CStdString strSQL = FormatSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2320     m_pDS->exec(strSQL.c_str());
2321     strSQL = FormatSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2322     m_pDS->exec(strSQL.c_str());
2323   }
2324   catch (...)
2325   {
2326     CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2327   }
2328 }
2329
2330 //********************************************************************************************************************************
2331 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2332 {
2333   try
2334   {
2335     if (NULL == m_pDB.get()) return ;
2336     if (NULL == m_pDS.get()) return ;
2337     long lMovieId = GetMovieId(strFilenameAndPath);
2338     if (lMovieId < 0)
2339     {
2340       return ;
2341     }
2342
2343     long lFileId = GetFileId(strFilenameAndPath);
2344     if (lFileId < 0)
2345       return ;
2346
2347     BeginTransaction();
2348
2349     CStdString strSQL;
2350     strSQL=FormatSQL("delete from genrelinkmovie where idmovie=%i", lMovieId);
2351     m_pDS->exec(strSQL.c_str());
2352
2353     strSQL=FormatSQL("delete from actorlinkmovie where idmovie=%i", lMovieId);
2354     m_pDS->exec(strSQL.c_str());
2355
2356     strSQL=FormatSQL("delete from directorlinkmovie where idmovie=%i", lMovieId);
2357     m_pDS->exec(strSQL.c_str());
2358
2359     strSQL=FormatSQL("delete from studiolinkmovie where idmovie=%i", lMovieId);
2360     m_pDS->exec(strSQL.c_str());
2361
2362     if (!bKeepThumb)
2363       DeleteThumbForItem(strFilenameAndPath,false);
2364
2365     DeleteStreamDetails(lFileId);
2366
2367     // keep the movie table entry, linking to tv shows, and bookmarks
2368     // so we can update the data in place
2369     // the ancilliary tables are still purged
2370     if (!bKeepId)
2371     {
2372       ClearBookMarksOfFile(strFilenameAndPath);
2373
2374       strSQL=FormatSQL("delete from movie where idmovie=%i", lMovieId);
2375       m_pDS->exec(strSQL.c_str());
2376
2377       strSQL=FormatSQL("delete from movielinktvshow where idmovie=%i", lMovieId);
2378       m_pDS->exec(strSQL.c_str());
2379     }
2380     /*
2381     // work in progress
2382     else
2383     {
2384       // clear the title
2385       strSQL=FormatSQL("update movie set c%02d=NULL where idmovie=%i", VIDEODB_ID_TITLE, lMovieId);
2386       m_pDS->exec(strSQL.c_str());
2387     }
2388     */
2389
2390     CStdString strPath, strFileName;
2391     SplitPath(strFilenameAndPath,strPath,strFileName);
2392     InvalidatePathHash(strPath);
2393     CommitTransaction();
2394   }
2395   catch (...)
2396   {
2397     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2398   }
2399 }
2400
2401 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2402 {
2403   try
2404   {
2405     long lTvShowId=-1;
2406     if (NULL == m_pDB.get()) return ;
2407     if (NULL == m_pDS.get()) return ;
2408     lTvShowId = GetTvShowId(strPath);
2409     if (lTvShowId < 0)
2410     {
2411       return ;
2412     }
2413
2414     BeginTransaction();
2415
2416     CStdString strSQL=FormatSQL("select tvshowlinkepisode.idepisode,path.strPath,files.strFileName from tvshowlinkepisode,path,files,episode where tvshowlinkepisode.idshow=%u and tvshowlinkepisode.idepisode=episode.idEpisode and episode.idFile=files.idFile and files.idPath=path.idPath",lTvShowId);
2417     m_pDS2->query(strSQL.c_str());
2418     while (!m_pDS2->eof())
2419     {
2420       CStdString strPath;
2421       CUtil::AddFileToFolder(m_pDS2->fv("path.strPath").get_asString(),m_pDS2->fv("files.strFilename").get_asString(),strPath);
2422       DeleteEpisode(strPath,m_pDS2->fv(0).get_asLong(), bKeepId);
2423       m_pDS2->next();
2424     }
2425
2426     strSQL=FormatSQL("delete from genrelinktvshow where idshow=%i", lTvShowId);
2427     m_pDS->exec(strSQL.c_str());
2428
2429     strSQL=FormatSQL("delete from actorlinktvshow where idshow=%i", lTvShowId);
2430     m_pDS->exec(strSQL.c_str());
2431
2432     strSQL=FormatSQL("delete from directorlinktvshow where idshow=%i", lTvShowId);
2433     m_pDS->exec(strSQL.c_str());
2434
2435     strSQL=FormatSQL("delete from tvshowlinkpath where idshow=%u", lTvShowId);
2436     m_pDS->exec(strSQL.c_str());
2437
2438     strSQL=FormatSQL("delete from studiolinktvshow where idshow=%i", lTvShowId);
2439     m_pDS->exec(strSQL.c_str());
2440
2441     if (!bKeepThumb)
2442       DeleteThumbForItem(strPath,true);
2443
2444     // keep tvshow table and movielink table so we can update data in place
2445     if (!bKeepId)
2446     {
2447       strSQL=FormatSQL("delete from tvshow where idshow=%i", lTvShowId);
2448       m_pDS->exec(strSQL.c_str());
2449
2450       strSQL=FormatSQL("delete from movielinktvshow where idshow=%i", lTvShowId);
2451       m_pDS->exec(strSQL.c_str());
2452     }
2453     /*
2454     // work in progress
2455     else
2456     {
2457       // clear the title
2458       strSQL=FormatSQL("update tvshow set c%02d=NULL where idshow=%i", VIDEODB_ID_TV_TITLE, lTvShowId);
2459       m_pDS->exec(strSQL.c_str());
2460     }
2461     */
2462
2463     InvalidatePathHash(strPath);
2464
2465     CommitTransaction();
2466   }
2467   catch (...)
2468   {
2469     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2470   }
2471 }
2472
2473 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, long lEpisodeId, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2474 {
2475   try
2476   {
2477     if (NULL == m_pDB.get()) return ;
2478     if (NULL == m_pDS.get()) return ;
2479     if (lEpisodeId < 0)
2480     {
2481       lEpisodeId = GetEpisodeId(strFilenameAndPath);
2482       if (lEpisodeId < 0)
2483       {
2484         return ;
2485       }
2486     }
2487
2488     long lFileId = GetFileId(strFilenameAndPath);
2489     if (lFileId < 0)
2490       return ;
2491
2492     CStdString strSQL;
2493     strSQL=FormatSQL("delete from actorlinkepisode where idepisode=%i", lEpisodeId);
2494     m_pDS->exec(strSQL.c_str());
2495
2496     strSQL=FormatSQL("delete from directorlinkepisode where idepisode=%i", lEpisodeId);
2497     m_pDS->exec(strSQL.c_str());
2498
2499     strSQL=FormatSQL("select tvshowlinkepisode.idshow from tvshowlinkepisode where idepisode=%u",lEpisodeId);
2500     m_pDS->query(strSQL.c_str());
2501
2502     strSQL=FormatSQL("delete from tvshowlinkepisode where idepisode=%i", lEpisodeId);
2503     m_pDS->exec(strSQL.c_str());
2504
2505     if (!bKeepThumb)
2506       DeleteThumbForItem(strFilenameAndPath,false);
2507
2508     DeleteStreamDetails(lFileId);
2509
2510     // keep episode table entry and bookmarks so we can update the data in place
2511     // the ancilliary tables are still purged
2512     if (!bKeepId)
2513     {
2514       ClearBookMarksOfFile(strFilenameAndPath);
2515
2516       strSQL=FormatSQL("delete from episode where idfile=%i", lFileId);
2517       m_pDS->exec(strSQL.c_str());
2518     }
2519     /*
2520     // work in progress
2521     else
2522     {
2523       // clear the title
2524       strSQL=FormatSQL("update episode set c%02d=NULL where idepisode=%i", VIDEODB_ID_EPISODE_TITLE, lEpisodeId);
2525       m_pDS->exec(strSQL.c_str());
2526     }
2527     */
2528
2529   }
2530   catch (...)
2531   {
2532     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2533   }
2534 }
2535
2536 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, bool bKeepThumb /* = false */)
2537 {
2538   try
2539   {
2540     if (NULL == m_pDB.get()) return ;
2541     if (NULL == m_pDS.get()) return ;
2542     long lMVideoId = GetMusicVideoId(strFilenameAndPath);
2543     if (lMVideoId < 0)
2544     {
2545       return ;
2546     }
2547
2548     long lFileId = GetFileId(strFilenameAndPath);
2549     if (lFileId < 0)
2550       return ;
2551  
2552     BeginTransaction();
2553
2554     CStdString strSQL;
2555     strSQL=FormatSQL("delete from genrelinkmusicvideo where idmvideo=%i", lMVideoId);
2556     m_pDS->exec(strSQL.c_str());
2557
2558     strSQL=FormatSQL("delete from artistlinkmusicvideo where idmvideo=%i", lMVideoId);
2559     m_pDS->exec(strSQL.c_str());
2560
2561     strSQL=FormatSQL("delete from directorlinkmusicvideo where idmvideo=%i", lMVideoId);
2562     m_pDS->exec(strSQL.c_str());
2563
2564     strSQL=FormatSQL("delete from studiolinkmusicvideo where idmvideo=%i", lMVideoId);
2565     m_pDS->exec(strSQL.c_str());
2566
2567     if (!bKeepThumb)
2568       DeleteThumbForItem(strFilenameAndPath,false);
2569
2570     DeleteStreamDetails(lFileId);
2571
2572     // keep the music video table entry and bookmarks so we can update data in place
2573     // the ancilliary tables are still purged
2574     if (!bKeepId)
2575     {
2576       ClearBookMarksOfFile(strFilenameAndPath);
2577
2578       strSQL=FormatSQL("delete from musicvideo where idmvideo=%i", lMVideoId);
2579       m_pDS->exec(strSQL.c_str());
2580     }
2581     /*
2582     // work in progress
2583     else
2584     {
2585       // clear the title
2586       strSQL=FormatSQL("update musicvideo set c%02d=NULL where idmvideo=%i", VIDEODB_ID_MUSICVIDEO_TITLE, lMVideoId);
2587       m_pDS->exec(strSQL.c_str());
2588     }
2589     */
2590
2591     CStdString strPath, strFileName;
2592     SplitPath(strFilenameAndPath,strPath,strFileName);
2593     InvalidatePathHash(strPath);
2594     CommitTransaction();
2595   }
2596   catch (...)
2597   {
2598     CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2599   }
2600 }
2601
2602 void CVideoDatabase::DeleteStreamDetails(long lFileId)
2603 {
2604     m_pDS->exec(FormatSQL("delete from streamdetails where idFile=%u", lFileId));
2605 }
2606
2607 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details)
2608 {
2609   for (int i = min + 1; i < max; i++)
2610   {
2611     switch (offsets[i].type)
2612     {
2613     case VIDEODB_TYPE_STRING:
2614       *(CStdString*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+1).get_asString();
2615       break;
2616     case VIDEODB_TYPE_INT:
2617     case VIDEODB_TYPE_COUNT:
2618       *(int*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+1).get_asInteger();
2619       break;
2620     case VIDEODB_TYPE_BOOL:
2621       *(bool*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+1).get_asBool();
2622       break;
2623     case VIDEODB_TYPE_FLOAT:
2624       *(float*)(((char*)&details)+offsets[i].offset) = pDS->fv(i+1).get_asFloat();
2625       break;
2626     }
2627   }
2628 }
2629
2630 DWORD movieTime = 0;
2631 DWORD castTime = 0;
2632
2633 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, long id)
2634 {
2635   CVideoInfoTag details;
2636   details.Reset();
2637
2638   switch (type)
2639   {
2640     case VIDEODB_CONTENT_MOVIES:
2641       GetMovieInfo("", details, id);
2642       break;
2643     case VIDEODB_CONTENT_TVSHOWS:
2644       GetTvShowInfo("", details, id);
2645       break;
2646     case VIDEODB_CONTENT_EPISODES:
2647       GetEpisodeInfo("", details, id);
2648       break;
2649     case VIDEODB_CONTENT_MUSICVIDEOS:
2650       GetMusicVideoInfo("", details, id);
2651   }
2652
2653   return details;
2654 }
2655
2656 bool CVideoDatabase::GetStreamDetailsForFileId(CStreamDetails& details, long lFileId) const
2657 {
2658   if (lFileId < 0)
2659     return false;
2660
2661   bool retVal = false;
2662
2663   dbiplus::Dataset *pDS = m_pDB->CreateDataset();
2664   CStdString strSQL = FormatSQL("SELECT * FROM streamdetails WHERE idFile = %u", lFileId);
2665   pDS->query(strSQL);
2666
2667   details.Reset();
2668   while (!pDS->eof())
2669   {
2670     CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInteger();
2671     switch (e)
2672     {
2673     case CStreamDetail::VIDEO:
2674       {
2675         CStreamDetailVideo *p = new CStreamDetailVideo();
2676         p->m_strCodec = pDS->fv(2).get_asString();
2677         p->m_fAspect = pDS->fv(3).get_asFloat();
2678         p->m_iWidth = pDS->fv(4).get_asInteger();
2679         p->m_iHeight = pDS->fv(5).get_asInteger();
2680         details.AddStream(p);
2681         retVal = true;
2682         break;
2683       }
2684     case CStreamDetail::AUDIO:
2685       {
2686         CStreamDetailAudio *p = new CStreamDetailAudio();
2687         p->m_strCodec = pDS->fv(6).get_asString();
2688         if (pDS->fv(7).get_isNull())
2689           p->m_iChannels = -1;
2690         else
2691           p->m_iChannels = pDS->fv(7).get_asInteger();
2692         p->m_strLanguage = pDS->fv(8).get_asString();
2693         details.AddStream(p);
2694         retVal = true;
2695         break;
2696       }
2697     case CStreamDetail::SUBTITLE:
2698       {
2699         CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
2700         p->m_strLanguage = pDS->fv(9).get_asString();
2701         details.AddStream(p);
2702         retVal = true;
2703         break;
2704       }
2705     }
2706
2707     pDS->next();
2708   }
2709
2710   pDS->close();
2711   details.DetermineBestStreams();
2712
2713   return retVal;
2714 }
2715
2716 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2717 {
2718   CVideoInfoTag details;
2719   details.Reset();
2720
2721   DWORD time = timeGetTime();
2722   long lMovieId = pDS->fv(0).get_asLong();
2723
2724   GetDetailsFromDB(pDS, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
2725
2726   details.m_iDbId = lMovieId;
2727   GetCommonDetails(pDS, details);
2728   movieTime += timeGetTime() - time; time = timeGetTime();
2729
2730   GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2731
2732   if (needsCast)
2733   {
2734     // create cast string
2735     CStdString strSQL = FormatSQL("select  actors.strActor,actorlinkmovie.strRole,actors.strThumb from  actorlinkmovie,actors where actorlinkmovie.idMovie=%u and actorlinkmovie.idActor  = actors.idActor order by actorlinkmovie.ROWID",lMovieId);
2736     m_pDS2->query(strSQL.c_str());
2737     while (!m_pDS2->eof())
2738     {
2739       SActorInfo info;
2740       info.strName = m_pDS2->fv("actors.strActor").get_asString();
2741       info.strRole = m_pDS2->fv("actorlinkmovie.strRole").get_asString();
2742       info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2743       details.m_cast.push_back(info);
2744       m_pDS2->next();
2745     }
2746     castTime += timeGetTime() - time; time = timeGetTime();
2747     details.m_strPictureURL.Parse();
2748   }
2749   return details;
2750 }
2751
2752 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2753 {
2754   CVideoInfoTag details;
2755   details.Reset();
2756
2757   DWORD time = timeGetTime();
2758   long lTvShowId = pDS->fv(0).get_asLong();
2759
2760   GetDetailsFromDB(pDS, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details);
2761   details.m_iDbId = lTvShowId;
2762   details.m_strPath = pDS->fv(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
2763   details.m_iEpisode = m_pDS->fv(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInteger();
2764   details.m_playCount = m_pDS->fv(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInteger();
2765   details.m_strShowTitle = details.m_strTitle;
2766
2767   movieTime += timeGetTime() - time; time = timeGetTime();
2768
2769   if (needsCast)
2770   {
2771     // create cast string
2772     CStdString strSQL = FormatSQL("select actors.strActor,actorlinktvshow.strRole,actors.strThumb from actorlinktvshow,actors where actorlinktvshow.idShow=%u and actorlinktvshow.idActor = actors.idActor",lTvShowId);
2773     m_pDS2->query(strSQL.c_str());
2774     while (!m_pDS2->eof())
2775     {
2776       SActorInfo info;
2777       info.strName = m_pDS2->fv("actors.strActor").get_asString();
2778       info.strRole = m_pDS2->fv("actorlinktvshow.strRole").get_asString();
2779       info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2780       details.m_cast.push_back(info);
2781       m_pDS2->next();
2782     }
2783     castTime += timeGetTime() - time; time = timeGetTime();
2784     details.m_strPictureURL.Parse();
2785   }
2786   details.m_fanart.Unpack();
2787   return details;
2788 }
2789
2790 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
2791 {
2792   CVideoInfoTag details;
2793   details.Reset();
2794
2795   DWORD time = timeGetTime();
2796   long lEpisodeId = pDS->fv(0).get_asLong();
2797
2798   GetDetailsFromDB(pDS, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
2799   details.m_iDbId = lEpisodeId;
2800   GetCommonDetails(pDS, details);
2801   movieTime += timeGetTime() - time; time = timeGetTime();
2802
2803   details.m_strShowTitle = pDS->fv(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
2804   details.m_strStudio = pDS->fv(VIDEODB_DETAILS_EPISODE_STUDIO).get_asString();
2805
2806   GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2807
2808   if (needsCast)
2809   {
2810     // create cast string
2811     CStdString strSQL = FormatSQL("select actors.strActor,actorlinkepisode.strRole,actors.strThumb from actorlinkepisode,actors where actorlinkepisode.idEpisode=%u and actorlinkepisode.idActor = actors.idActor",lEpisodeId);
2812     m_pDS2->query(strSQL.c_str());
2813     bool showCast=false;
2814     while (!m_pDS2->eof() || !showCast)
2815     {
2816       if (!m_pDS2->eof())
2817       {
2818         SActorInfo info;
2819         info.strName = m_pDS2->fv("actors.strActor").get_asString();
2820         info.strRole = m_pDS2->fv("actorlinkepisode.strRole").get_asString();
2821         info.thumbUrl.ParseString(m_pDS2->fv("actors.strThumb").get_asString());
2822         details.m_cast.push_back(info);
2823         m_pDS2->next();
2824       }
2825       if (m_pDS2->eof() && !showCast)
2826       {
2827         showCast = true;
2828         long idShow = GetTvShowForEpisode(details.m_iDbId);
2829         if (idShow > -1)
2830         {
2831           strSQL = FormatSQL("select actors.strActor,actorlinktvshow.strRole,actors.strThumb from actorlinktvshow,actors where actorlinktvshow.idShow=%u and actorlinktvshow.idActor = actors.idActor",idShow);
2832           m_pDS2->query(strSQL.c_str());
2833         }
2834       }
2835     }
2836     castTime += timeGetTime() - time; time = timeGetTime();
2837     m_pDS2->close();
2838     details.m_strPictureURL.Parse();
2839   }
2840   return details;
2841 }
2842
2843 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS)
2844 {
2845   CVideoInfoTag details;
2846   details.Reset();
2847
2848   DWORD time = timeGetTime();
2849   long lMovieId = pDS->fv(0).get_asLong();
2850
2851   GetDetailsFromDB(pDS, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
2852   details.m_iDbId = lMovieId;
2853   GetCommonDetails(pDS, details);
2854   movieTime += timeGetTime() - time; time = timeGetTime();
2855
2856   GetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2857
2858   details.m_strPictureURL.Parse();
2859   return details;
2860 }
2861
2862 void CVideoDatabase::GetCommonDetails(auto_ptr<Dataset> &pDS, CVideoInfoTag &details)
2863 {
2864   details.m_iFileId = pDS->fv(VIDEODB_DETAILS_FILEID).get_asLong();
2865   details.m_strPath = pDS->fv(VIDEODB_DETAILS_PATH).get_asString();
2866   CStdString strFileName = pDS->fv(VIDEODB_DETAILS_FILE).get_asString();
2867   ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
2868   details.m_playCount = pDS->fv(VIDEODB_DETAILS_PLAYCOUNT).get_asInteger();
2869   details.m_lastPlayed = pDS->fv(VIDEODB_DETAILS_LASTPLAYED).get_asString();
2870 }
2871
2872 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
2873 /// \retval Returns true if the settings exist, false otherwise.
2874 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
2875 {
2876   try
2877   {
2878     // obtain the FileID (if it exists)
2879 #ifdef NEW_VIDEODB_METHODS
2880     if (NULL == m_pDB.get()) return false;
2881     if (NULL == m_pDS.get()) return false;
2882     CStdString strPath, strFileName;
2883     CUtil::Split(strFilenameAndPath, strPath, strFileName);
2884     CStdString strSQL=FormatSQL("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());
2885 #else
2886     long lFileId = GetFileId(strFilenameAndPath);
2887     if (lFileId < 0) return false;
2888     if (NULL == m_pDB.get()) return false;
2889     if (NULL == m_pDS.get()) return false;
2890     // ok, now obtain the settings for this file
2891     CStdString strSQL=FormatSQL("select * from settings where settings.idFile = '%i'", lFileId);
2892 #endif
2893     m_pDS->query( strSQL.c_str() );
2894     if (m_pDS->num_rows() > 0)
2895     { // get the video settings info
2896       settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
2897       settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInteger();
2898       settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
2899       settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
2900      &