2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "threads/SystemClock.h"
22 #include "VideoDatabase.h"
23 #include "video/windows/GUIWindowVideoBase.h"
24 #include "utils/RegExp.h"
25 #include "addons/AddonManager.h"
26 #include "GUIInfoManager.h"
28 #include "utils/URIUtils.h"
29 #include "utils/XMLUtils.h"
30 #include "GUIPassword.h"
31 #include "filesystem/StackDirectory.h"
32 #include "filesystem/MultiPathDirectory.h"
33 #include "VideoInfoScanner.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "filesystem/Directory.h"
36 #include "filesystem/File.h"
37 #include "filesystem/SpecialProtocol.h"
38 #include "dialogs/GUIDialogExtendedProgressBar.h"
39 #include "dialogs/GUIDialogProgress.h"
40 #include "dialogs/GUIDialogYesNo.h"
42 #include "profiles/ProfilesManager.h"
43 #include "settings/AdvancedSettings.h"
44 #include "settings/MediaSettings.h"
45 #include "settings/MediaSourceSettings.h"
46 #include "settings/Settings.h"
47 #include "utils/StringUtils.h"
48 #include "guilib/LocalizeStrings.h"
49 #include "utils/TimeUtils.h"
50 #include "utils/log.h"
51 #include "TextureCache.h"
52 #include "addons/AddonInstaller.h"
53 #include "interfaces/AnnouncementManager.h"
54 #include "dbwrappers/dataset.h"
55 #include "utils/LabelFormatter.h"
56 #include "XBDateTime.h"
58 #include "video/VideoDbUrl.h"
59 #include "playlists/SmartPlayList.h"
60 #include "utils/GroupUtils.h"
63 using namespace dbiplus;
64 using namespace XFILE;
65 using namespace VIDEO;
66 using namespace ADDON;
68 //********************************************************************************************************************************
69 CVideoDatabase::CVideoDatabase(void)
73 //********************************************************************************************************************************
74 CVideoDatabase::~CVideoDatabase(void)
77 //********************************************************************************************************************************
78 bool CVideoDatabase::Open()
80 return CDatabase::Open(g_advancedSettings.m_databaseVideo);
83 bool CVideoDatabase::CreateTables()
85 /* indexes should be added on any columns that are used in in */
86 /* a where or a join. primary key on a column is the same as a */
87 /* unique index on that column, so there is no need to add any */
88 /* index if no other columns are refered */
90 /* order of indexes are important, for an index to be considered all */
91 /* columns up to the column in question have to have been specified */
92 /* select * from actorlinkmovie where idMovie = 1, can not take */
93 /* advantage of a index that has been created on ( idGenre, idMovie ) */
94 /*, hower on on ( idMovie, idGenre ) will be considered for use */
99 CDatabase::CreateTables();
101 CLog::Log(LOGINFO, "create bookmark table");
102 m_pDS->exec("CREATE TABLE bookmark ( idBookmark integer primary key, idFile integer, timeInSeconds double, totalTimeInSeconds double, thumbNailImage text, player text, playerState text, type integer)\n");
103 m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
105 CLog::Log(LOGINFO, "create settings table");
106 m_pDS->exec("CREATE TABLE settings ( idFile integer, Deinterlace bool,"
107 "ViewMode integer,ZoomAmount float, PixelRatio float, VerticalShift float, AudioStream integer, SubtitleStream integer,"
108 "SubtitleDelay float, SubtitlesOn bool, Brightness float, Contrast float, Gamma float,"
109 "VolumeAmplification float, AudioDelay float, OutputToAllSpeakers bool, ResumeTime integer, Crop bool, CropLeft integer,"
110 "CropRight integer, CropTop integer, CropBottom integer, Sharpness float, NoiseReduction float, NonLinStretch bool, PostProcess bool,"
111 "ScalingMethod integer, DeinterlaceMode integer, StereoMode integer, StereoInvert bool)\n");
112 m_pDS->exec("CREATE UNIQUE INDEX ix_settings ON settings ( idFile )\n");
114 CLog::Log(LOGINFO, "create stacktimes table");
115 m_pDS->exec("CREATE TABLE stacktimes (idFile integer, times text)\n");
116 m_pDS->exec("CREATE UNIQUE INDEX ix_stacktimes ON stacktimes ( idFile )\n");
118 CLog::Log(LOGINFO, "create genre table");
119 m_pDS->exec("CREATE TABLE genre ( idGenre integer primary key, strGenre text)\n");
121 CLog::Log(LOGINFO, "create genrelinkmovie table");
122 m_pDS->exec("CREATE TABLE genrelinkmovie ( idGenre integer, idMovie integer)\n");
123 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_1 ON genrelinkmovie ( idGenre, idMovie)\n");
124 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_2 ON genrelinkmovie ( idMovie, idGenre)\n");
126 CLog::Log(LOGINFO, "create country table");
127 m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
129 CLog::Log(LOGINFO, "create countrylinkmovie table");
130 m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
131 m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
132 m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
134 CLog::Log(LOGINFO, "create movie table");
135 CStdString columns = "CREATE TABLE movie ( idMovie integer primary key, idFile integer";
137 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
138 columns += StringUtils::Format(",c%02d text", i);
140 columns += ", idSet integer)";
141 m_pDS->exec(columns.c_str());
142 m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
143 m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
145 CLog::Log(LOGINFO, "create actorlinkmovie table");
146 m_pDS->exec("CREATE TABLE actorlinkmovie ( idActor integer, idMovie integer, strRole text, iOrder integer)\n");
147 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_1 ON actorlinkmovie ( idActor, idMovie )\n");
148 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_2 ON actorlinkmovie ( idMovie, idActor )\n");
150 CLog::Log(LOGINFO, "create directorlinkmovie table");
151 m_pDS->exec("CREATE TABLE directorlinkmovie ( idDirector integer, idMovie integer)\n");
152 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_1 ON directorlinkmovie ( idDirector, idMovie )\n");
153 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_2 ON directorlinkmovie ( idMovie, idDirector )\n");
155 CLog::Log(LOGINFO, "create writerlinkmovie table");
156 m_pDS->exec("CREATE TABLE writerlinkmovie ( idWriter integer, idMovie integer)\n");
157 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_1 ON writerlinkmovie ( idWriter, idMovie )\n");
158 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_2 ON writerlinkmovie ( idMovie, idWriter )\n");
160 CLog::Log(LOGINFO, "create actors table");
161 m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
163 CLog::Log(LOGINFO, "create path table");
164 m_pDS->exec("CREATE TABLE path ( idPath integer primary key, strPath text, strContent text, strScraper text, strHash text, scanRecursive integer, useFolderNames bool, strSettings text, noUpdate bool, exclude bool, dateAdded text)");
165 m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
167 CLog::Log(LOGINFO, "create files table");
168 m_pDS->exec("CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text, dateAdded text)");
169 m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
171 CLog::Log(LOGINFO, "create tvshow table");
172 columns = "CREATE TABLE tvshow ( idShow integer primary key";
174 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
175 columns += StringUtils::Format(",c%02d text", i);;
178 m_pDS->exec(columns.c_str());
180 CLog::Log(LOGINFO, "create directorlinktvshow table");
181 m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
182 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
183 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
185 CLog::Log(LOGINFO, "create actorlinktvshow table");
186 m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text, iOrder integer)\n");
187 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
188 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
190 CLog::Log(LOGINFO, "create studiolinktvshow table");
191 m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
192 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
193 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
195 CLog::Log(LOGINFO, "create episode table");
196 columns = "CREATE TABLE episode ( idEpisode integer primary key, idFile integer";
197 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
200 if ( i == VIDEODB_ID_EPISODE_SEASON || i == VIDEODB_ID_EPISODE_EPISODE || i == VIDEODB_ID_EPISODE_BOOKMARK)
201 column = StringUtils::Format(",c%02d varchar(24)", i);
203 column = StringUtils::Format(",c%02d text", i);
207 columns += ", idShow integer)";
208 m_pDS->exec(columns.c_str());
209 m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
210 m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
211 CStdString createColIndex = StringUtils::Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
212 m_pDS->exec(createColIndex.c_str());
213 createColIndex = StringUtils::Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
214 m_pDS->exec(createColIndex.c_str());
215 m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
216 m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
218 CLog::Log(LOGINFO, "create tvshowlinkpath table");
219 m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
220 m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
221 m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
223 CLog::Log(LOGINFO, "create actorlinkepisode table");
224 m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text, iOrder integer)\n");
225 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
226 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
228 CLog::Log(LOGINFO, "create directorlinkepisode table");
229 m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
230 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
231 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
233 CLog::Log(LOGINFO, "create writerlinkepisode table");
234 m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
235 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
236 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
238 CLog::Log(LOGINFO, "create genrelinktvshow table");
239 m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
240 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
241 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
243 CLog::Log(LOGINFO, "create movielinktvshow table");
244 m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
245 m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
246 m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
248 CLog::Log(LOGINFO, "create studio table");
249 m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
251 CLog::Log(LOGINFO, "create studiolinkmovie table");
252 m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
253 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
254 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
256 CLog::Log(LOGINFO, "create musicvideo table");
257 columns = "CREATE TABLE musicvideo ( idMVideo integer primary key, idFile integer";
258 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
259 columns += StringUtils::Format(",c%02d text", i);;
262 m_pDS->exec(columns.c_str());
264 m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
265 m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
267 CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
268 m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
269 m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
270 m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
272 CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
273 m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
274 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
275 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
277 CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
278 m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
279 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
280 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
282 CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
283 m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
284 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
285 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
287 CLog::Log(LOGINFO, "create streaminfo table");
288 m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
289 "strVideoCodec text, fVideoAspect float, iVideoWidth integer, iVideoHeight integer, "
290 "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text, iVideoDuration integer, strStereoMode text)");
291 m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
293 CLog::Log(LOGINFO, "create sets table");
294 m_pDS->exec("CREATE TABLE sets ( idSet integer primary key, strSet text)\n");
296 // create basepath indices
297 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
298 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
299 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
300 m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c17(12) )");
302 CLog::Log(LOGINFO, "create seasons table");
303 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
304 m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
306 CLog::Log(LOGINFO, "create art table");
307 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
308 m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
310 CLog::Log(LOGINFO, "create tag table");
311 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
312 m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
314 CLog::Log(LOGINFO, "create taglinks table");
315 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
316 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
317 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
318 m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
320 CLog::Log(LOGINFO, "create deletion triggers");
321 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
322 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
323 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
325 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
326 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
327 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
329 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
330 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
331 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
333 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
334 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
336 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
337 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
339 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
340 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
342 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
343 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
345 m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
346 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
349 // we create views last to ensure all indexes are rolled in
354 CLog::Log(LOGERROR, "%s unable to create tables:%i", __FUNCTION__, (int)GetLastError());
355 RollbackTransaction();
362 void CVideoDatabase::CreateViews()
364 CLog::Log(LOGINFO, "create episodeview");
365 m_pDS->exec("DROP VIEW IF EXISTS episodeview");
366 CStdString episodeview = PrepareSQL("CREATE VIEW episodeview AS SELECT "
368 " files.strFileName AS strFileName,"
369 " path.strPath AS strPath,"
370 " files.playCount AS playCount,"
371 " files.lastPlayed AS lastPlayed,"
372 " files.dateAdded AS dateAdded,"
373 " tvshow.c%02d AS strTitle,"
374 " tvshow.c%02d AS strStudio,"
375 " tvshow.c%02d AS premiered,"
376 " tvshow.c%02d AS mpaa,"
377 " tvshow.c%02d AS strShowPath, "
378 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
379 " bookmark.totalTimeInSeconds AS totalTimeInSeconds, "
380 " seasons.idSeason AS idSeason "
383 " files.idFile=episode.idFile"
385 " tvshow.idShow=episode.idShow"
386 " LEFT JOIN seasons ON"
387 " seasons.idShow=episode.idShow AND seasons.season=episode.c%02d"
389 " files.idPath=path.idPath"
390 " LEFT JOIN bookmark ON"
391 " bookmark.idFile=episode.idFile AND bookmark.type=1", VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_PREMIERED, VIDEODB_ID_TV_MPAA, VIDEODB_ID_TV_BASEPATH, VIDEODB_ID_EPISODE_SEASON);
392 m_pDS->exec(episodeview.c_str());
394 CLog::Log(LOGINFO, "create tvshowview");
395 m_pDS->exec("DROP VIEW IF EXISTS tvshowview");
396 CStdString tvshowview = PrepareSQL("CREATE VIEW tvshowview AS SELECT "
398 " path.strPath AS strPath,"
399 " path.dateAdded AS dateAdded,"
400 " MAX(files.lastPlayed) AS lastPlayed,"
401 " NULLIF(COUNT(episode.c12), 0) AS totalCount,"
402 " COUNT(files.playCount) AS watchedcount,"
403 " NULLIF(COUNT(DISTINCT(episode.c12)), 0) AS totalSeasons "
405 " LEFT JOIN tvshowlinkpath ON"
406 " tvshowlinkpath.idShow=tvshow.idShow"
408 " path.idPath=tvshowlinkpath.idPath"
409 " LEFT JOIN episode ON"
410 " episode.idShow=tvshow.idShow"
411 " LEFT JOIN files ON"
412 " files.idFile=episode.idFile "
413 "GROUP BY tvshow.idShow;");
414 m_pDS->exec(tvshowview.c_str());
416 CLog::Log(LOGINFO, "create musicvideoview");
417 m_pDS->exec("DROP VIEW IF EXISTS musicvideoview");
418 m_pDS->exec("CREATE VIEW musicvideoview AS SELECT"
420 " files.strFileName as strFileName,"
421 " path.strPath as strPath,"
422 " files.playCount as playCount,"
423 " files.lastPlayed as lastPlayed,"
424 " files.dateAdded as dateAdded, "
425 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
426 " bookmark.totalTimeInSeconds AS totalTimeInSeconds "
429 " files.idFile=musicvideo.idFile"
431 " path.idPath=files.idPath"
432 " LEFT JOIN bookmark ON"
433 " bookmark.idFile=musicvideo.idFile AND bookmark.type=1");
435 CLog::Log(LOGINFO, "create movieview");
436 m_pDS->exec("DROP VIEW IF EXISTS movieview");
437 m_pDS->exec("CREATE VIEW movieview AS SELECT"
439 " sets.strSet AS strSet,"
440 " files.strFileName AS strFileName,"
441 " path.strPath AS strPath,"
442 " files.playCount AS playCount,"
443 " files.lastPlayed AS lastPlayed, "
444 " files.dateAdded AS dateAdded, "
445 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
446 " bookmark.totalTimeInSeconds AS totalTimeInSeconds "
449 " sets.idSet = movie.idSet"
451 " files.idFile=movie.idFile"
453 " path.idPath=files.idPath"
454 " LEFT JOIN bookmark ON"
455 " bookmark.idFile=movie.idFile AND bookmark.type=1");
458 //********************************************************************************************************************************
459 int CVideoDatabase::GetPathId(const CStdString& strPath)
465 if (NULL == m_pDB.get()) return -1;
466 if (NULL == m_pDS.get()) return -1;
468 CStdString strPath1(strPath);
469 if (URIUtils::IsStack(strPath) || StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://"))
470 URIUtils::GetParentPath(strPath,strPath1);
472 URIUtils::AddSlashAtEnd(strPath1);
474 strSQL=PrepareSQL("select idPath from path where strPath='%s'",strPath1.c_str());
475 m_pDS->query(strSQL.c_str());
477 idPath = m_pDS->fv("path.idPath").get_asInt();
484 CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
489 bool CVideoDatabase::GetPaths(set<CStdString> &paths)
493 if (NULL == m_pDB.get()) return false;
494 if (NULL == m_pDS.get()) return false;
498 // grab all paths with movie content set
499 if (!m_pDS->query("select strPath,noUpdate from path"
500 " where (strContent = 'movies' or strContent = 'musicvideos')"
501 " and strPath NOT like 'multipath://%%'"
502 " order by strPath"))
505 while (!m_pDS->eof())
507 if (!m_pDS->fv("noUpdate").get_asBool())
508 paths.insert(m_pDS->fv("strPath").get_asString());
513 // then grab all tvshow paths
514 if (!m_pDS->query("select strPath,noUpdate from path"
515 " where ( strContent = 'tvshows'"
516 " or idPath in (select idPath from tvshowlinkpath))"
517 " and strPath NOT like 'multipath://%%'"
518 " order by strPath"))
521 while (!m_pDS->eof())
523 if (!m_pDS->fv("noUpdate").get_asBool())
524 paths.insert(m_pDS->fv("strPath").get_asString());
529 // finally grab all other paths holding a movie which is not a stack or a rar archive
530 // - this isnt perfect but it should do fine in most situations.
531 // reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
532 // not making mistakes must take priority
533 if (!m_pDS->query("select strPath,noUpdate from path"
534 " where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
535 " and idPath NOT in (select idPath from tvshowlinkpath)"
536 " and idPath NOT in (select idPath from files where strFileName like 'video_ts.ifo')" // dvd folders get stacked to a single item in parent folder
537 " and idPath NOT in (select idPath from files where strFileName like 'index.bdmv')" // bluray folders get stacked to a single item in parent folder
538 " and strPath NOT like 'multipath://%%'"
539 " and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
540 " order by strPath"))
543 while (!m_pDS->eof())
545 if (!m_pDS->fv("noUpdate").get_asBool())
546 paths.insert(m_pDS->fv("strPath").get_asString());
554 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
559 bool CVideoDatabase::GetPathsForTvShow(int idShow, set<int>& paths)
564 if (NULL == m_pDB.get()) return false;
565 if (NULL == m_pDS.get()) return false;
566 strSQL = PrepareSQL("SELECT DISTINCT idPath FROM files JOIN episode ON episode.idFile=files.idFile WHERE episode.idShow=%i",idShow);
567 m_pDS->query(strSQL.c_str());
568 while (!m_pDS->eof())
570 paths.insert(m_pDS->fv(0).get_asInt());
578 CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
583 int CVideoDatabase::RunQuery(const CStdString &sql)
585 unsigned int time = XbmcThreads::SystemClockMillis();
587 if (m_pDS->query(sql.c_str()))
589 rows = m_pDS->num_rows();
593 CLog::Log(LOGDEBUG, "%s took %d ms for %d items query: %s", __FUNCTION__, XbmcThreads::SystemClockMillis() - time, rows, sql.c_str());
597 bool CVideoDatabase::GetSubPaths(const CStdString &basepath, vector< pair<int,string> >& subpaths)
602 if (!m_pDB.get() || !m_pDS.get())
605 CStdString path(basepath);
606 URIUtils::AddSlashAtEnd(path);
607 sql = PrepareSQL("SELECT idPath,strPath FROM path WHERE SUBSTR(strPath,1,%i)='%s'", StringUtils::utf8_strlen(path.c_str()), path.c_str());
608 m_pDS->query(sql.c_str());
609 while (!m_pDS->eof())
611 subpaths.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
619 CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, sql.c_str());
624 int CVideoDatabase::AddPath(const CStdString& strPath, const CStdString &strDateAdded /*= "" */)
629 int idPath = GetPathId(strPath);
631 return idPath; // already have the path
633 if (NULL == m_pDB.get()) return -1;
634 if (NULL == m_pDS.get()) return -1;
636 CStdString strPath1(strPath);
637 if (URIUtils::IsStack(strPath) || StringUtils::StartsWithNoCase(strPath, "rar://") || StringUtils::StartsWithNoCase(strPath, "zip://"))
638 URIUtils::GetParentPath(strPath,strPath1);
640 URIUtils::AddSlashAtEnd(strPath1);
642 // only set dateadded if we got one
643 if (!strDateAdded.empty())
644 strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper, dateAdded) values (NULL,'%s','','', '%s')", strPath1.c_str(), strDateAdded.c_str());
646 strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
647 m_pDS->exec(strSQL.c_str());
648 idPath = (int)m_pDS->lastinsertid();
653 CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
658 bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
662 if (NULL == m_pDB.get()) return false;
663 if (NULL == m_pDS.get()) return false;
665 CStdString strSQL=PrepareSQL("select strHash from path where strPath='%s'", path.c_str());
666 m_pDS->query(strSQL.c_str());
667 if (m_pDS->num_rows() == 0)
669 hash = m_pDS->fv("strHash").get_asString();
674 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
680 //********************************************************************************************************************************
681 int CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
683 CStdString strSQL = "";
687 if (NULL == m_pDB.get()) return -1;
688 if (NULL == m_pDS.get()) return -1;
690 CStdString strFileName, strPath;
691 SplitPath(strFileNameAndPath,strPath,strFileName);
693 int idPath = AddPath(strPath);
697 CStdString strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
699 m_pDS->query(strSQL.c_str());
700 if (m_pDS->num_rows() > 0)
702 idFile = m_pDS->fv("idFile").get_asInt() ;
708 strSQL=PrepareSQL("insert into files (idFile, idPath, strFileName) values(NULL, %i, '%s')", idPath, strFileName.c_str());
709 m_pDS->exec(strSQL.c_str());
710 idFile = (int)m_pDS->lastinsertid();
715 CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
720 int CVideoDatabase::AddFile(const CFileItem& item)
722 if (item.IsVideoDb() && item.HasVideoInfoTag())
723 return AddFile(item.GetVideoInfoTag()->m_strFileNameAndPath);
724 return AddFile(item.GetPath());
727 void CVideoDatabase::UpdateFileDateAdded(int idFile, const CStdString& strFileNameAndPath)
729 if (idFile < 0 || strFileNameAndPath.empty())
732 CStdString strSQL = "";
735 if (NULL == m_pDB.get()) return;
736 if (NULL == m_pDS.get()) return;
738 CStdString file = strFileNameAndPath;
739 if (URIUtils::IsStack(strFileNameAndPath))
740 file = CStackDirectory::GetFirstStackedFile(strFileNameAndPath);
742 if (URIUtils::IsInArchive(file))
743 file = CURL(file).GetHostName();
746 // Skip looking at the files ctime/mtime if defined by the user through as.xml
747 if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
749 // Let's try to get the modification datetime
750 struct __stat64 buffer;
751 if (CFile::Stat(file, &buffer) == 0 && (buffer.st_mtime != 0 || buffer.st_ctime !=0))
753 time_t now = time(NULL);
755 // Prefer the modification time if it's valid
756 if (g_advancedSettings.m_iVideoLibraryDateAdded == 1)
758 if (buffer.st_mtime != 0 && (time_t)buffer.st_mtime <= now)
759 addedTime = (time_t)buffer.st_mtime;
761 addedTime = (time_t)buffer.st_ctime;
763 // Use the newer of the creation and modification time
766 addedTime = max((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
767 // if the newer of the two dates is in the future, we try it with the older one
769 addedTime = min((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
772 // make sure the datetime does is not in the future
773 if (addedTime <= now)
775 struct tm *time = localtime(&addedTime);
782 if (!dateAdded.IsValid())
783 dateAdded = CDateTime::GetCurrentDateTime();
785 strSQL = PrepareSQL("update files set dateAdded='%s' where idFile=%d", dateAdded.GetAsDBDateTime().c_str(), idFile);
786 m_pDS->exec(strSQL.c_str());
790 CLog::Log(LOGERROR, "%s unable to update dateadded for file (%s)", __FUNCTION__, strSQL.c_str());
794 bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
798 if (NULL == m_pDB.get()) return false;
799 if (NULL == m_pDS.get()) return false;
802 { // this is an empty folder - we need only add it to the path table
803 // if the path actually exists
804 if (!CDirectory::Exists(path))
807 int idPath = AddPath(path);
808 if (idPath < 0) return false;
810 CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
811 m_pDS->exec(strSQL.c_str());
817 CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
823 bool CVideoDatabase::LinkMovieToTvshow(int idMovie, int idShow, bool bRemove)
827 if (NULL == m_pDB.get()) return false;
828 if (NULL == m_pDS.get()) return false;
830 if (bRemove) // delete link
832 CStdString strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i and idShow=%i", idMovie, idShow);
833 m_pDS->exec(strSQL.c_str());
837 CStdString strSQL=PrepareSQL("insert into movielinktvshow (idShow,idMovie) values (%i,%i)", idShow,idMovie);
838 m_pDS->exec(strSQL.c_str());
844 CLog::Log(LOGERROR, "%s (%i, %i) failed", __FUNCTION__, idMovie, idShow);
850 bool CVideoDatabase::IsLinkedToTvshow(int idMovie)
854 if (NULL == m_pDB.get()) return false;
855 if (NULL == m_pDS.get()) return false;
857 CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
858 m_pDS->query(strSQL.c_str());
870 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
876 bool CVideoDatabase::GetLinksToTvShow(int idMovie, vector<int>& ids)
880 if (NULL == m_pDB.get()) return false;
881 if (NULL == m_pDS.get()) return false;
883 CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
884 m_pDS2->query(strSQL.c_str());
885 while (!m_pDS2->eof())
887 ids.push_back(m_pDS2->fv(1).get_asInt());
896 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
903 //********************************************************************************************************************************
904 int CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
908 if (NULL == m_pDB.get()) return -1;
909 if (NULL == m_pDS.get()) return -1;
910 CStdString strPath, strFileName;
911 SplitPath(strFilenameAndPath,strPath,strFileName);
913 int idPath = GetPathId(strPath);
917 strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
918 m_pDS->query(strSQL.c_str());
919 if (m_pDS->num_rows() > 0)
921 int idFile = m_pDS->fv("files.idFile").get_asInt();
929 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
934 int CVideoDatabase::GetFileId(const CFileItem &item)
936 if (item.IsVideoDb() && item.HasVideoInfoTag())
937 return GetFileId(item.GetVideoInfoTag()->m_strFileNameAndPath);
938 return GetFileId(item.GetPath());
941 //********************************************************************************************************************************
942 int CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
946 if (NULL == m_pDB.get()) return -1;
947 if (NULL == m_pDS.get()) return -1;
950 // needed for query parameters
951 int idFile = GetFileId(strFilenameAndPath);
957 SplitPath(strFilenameAndPath,strPath,strFile);
959 // have to join movieinfo table for correct results
960 idPath = GetPathId(strPath);
961 if (idPath < 0 && strPath != strFilenameAndPath)
965 if (idFile == -1 && strPath != strFilenameAndPath)
970 strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
972 strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
974 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
975 m_pDS->query(strSQL.c_str());
976 if (m_pDS->num_rows() > 0)
977 idMovie = m_pDS->fv("idMovie").get_asInt();
984 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
989 int CVideoDatabase::GetTvShowId(const CStdString& strPath)
993 if (NULL == m_pDB.get()) return -1;
994 if (NULL == m_pDS.get()) return -1;
997 // have to join movieinfo table for correct results
998 int idPath = GetPathId(strPath);
1003 CStdString strPath1=strPath;
1004 CStdString strParent;
1007 strSQL=PrepareSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%i",idPath);
1008 m_pDS->query(strSQL);
1012 while (iFound == 0 && URIUtils::GetParentPath(strPath1, strParent))
1014 strSQL=PrepareSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and strPath='%s'",strParent.c_str());
1015 m_pDS->query(strSQL.c_str());
1018 int idShow = m_pDS->fv("idShow").get_asInt();
1022 strPath1 = strParent;
1025 if (m_pDS->num_rows() > 0)
1026 idTvShow = m_pDS->fv("idShow").get_asInt();
1033 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1038 int CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, int idEpisode, int idSeason) // input value is episode/season number hint - for multiparters
1042 if (NULL == m_pDB.get()) return -1;
1043 if (NULL == m_pDS.get()) return -1;
1045 // need this due to the nested GetEpisodeInfo query
1046 auto_ptr<Dataset> pDS;
1047 pDS.reset(m_pDB->CreateDataset());
1048 if (NULL == pDS.get()) return -1;
1050 int idFile = GetFileId(strFilenameAndPath);
1054 CStdString strSQL=PrepareSQL("select idEpisode from episode where idFile=%i", idFile);
1056 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
1057 pDS->query(strSQL.c_str());
1058 if (pDS->num_rows() > 0)
1060 if (idEpisode == -1)
1061 idEpisode = pDS->fv("episode.idEpisode").get_asInt();
1062 else // use the hint!
1067 int idTmpEpisode = pDS->fv("episode.idEpisode").get_asInt();
1068 GetEpisodeInfo(strFilenameAndPath,tag,idTmpEpisode);
1069 if (tag.m_iEpisode == idEpisode && (idSeason == -1 || tag.m_iSeason == idSeason)) {
1070 // match on the episode hint, and there's no season hint or a season hint match
1071 idEpisode = idTmpEpisode;
1089 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1094 int CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
1098 if (NULL == m_pDB.get()) return -1;
1099 if (NULL == m_pDS.get()) return -1;
1101 int idFile = GetFileId(strFilenameAndPath);
1105 CStdString strSQL=PrepareSQL("select idMVideo from musicvideo where idFile=%i", idFile);
1107 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, CURL::GetRedacted(strFilenameAndPath).c_str(), strSQL.c_str());
1108 m_pDS->query(strSQL.c_str());
1110 if (m_pDS->num_rows() > 0)
1111 idMVideo = m_pDS->fv("idMVideo").get_asInt();
1118 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1123 //********************************************************************************************************************************
1124 int CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
1128 if (NULL == m_pDB.get()) return -1;
1129 if (NULL == m_pDS.get()) return -1;
1131 int idMovie = GetMovieId(strFilenameAndPath);
1134 int idFile = AddFile(strFilenameAndPath);
1137 UpdateFileDateAdded(idFile, strFilenameAndPath);
1138 CStdString strSQL=PrepareSQL("insert into movie (idMovie, idFile) values (NULL, %i)", idFile);
1139 m_pDS->exec(strSQL.c_str());
1140 idMovie = (int)m_pDS->lastinsertid();
1147 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1152 int CVideoDatabase::AddTvShow(const CStdString& strPath)
1156 if (NULL == m_pDB.get()) return -1;
1157 if (NULL == m_pDS.get()) return -1;
1159 CStdString strSQL=PrepareSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath='%s' and path.idPath=tvshowlinkpath.idPath",strPath.c_str());
1160 m_pDS->query(strSQL.c_str());
1161 if (m_pDS->num_rows() != 0)
1162 return m_pDS->fv("tvshowlinkpath.idShow").get_asInt();
1164 strSQL=PrepareSQL("insert into tvshow (idShow) values (NULL)");
1165 m_pDS->exec(strSQL.c_str());
1166 int idTvShow = (int)m_pDS->lastinsertid();
1168 // Get the creation datetime of the tvshow directory
1169 CDateTime dateAdded;
1170 // Skip looking at the files ctime/mtime if defined by the user through as.xml
1171 if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
1173 struct __stat64 buffer;
1174 if (XFILE::CFile::Stat(strPath, &buffer) == 0)
1176 time_t now = time(NULL);
1177 // Make sure we have a valid date (i.e. not in the future)
1178 if ((time_t)buffer.st_ctime <= now)
1180 struct tm *time = localtime((const time_t*)&buffer.st_ctime);
1187 if (!dateAdded.IsValid())
1188 dateAdded = CDateTime::GetCurrentDateTime();
1190 int idPath = AddPath(strPath, dateAdded.GetAsDBDateTime());
1191 strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
1192 m_pDS->exec(strSQL.c_str());
1198 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1203 //********************************************************************************************************************************
1204 int CVideoDatabase::AddEpisode(int idShow, const CStdString& strFilenameAndPath)
1208 if (NULL == m_pDB.get()) return -1;
1209 if (NULL == m_pDS.get()) return -1;
1211 int idFile = AddFile(strFilenameAndPath);
1214 UpdateFileDateAdded(idFile, strFilenameAndPath);
1216 CStdString strSQL=PrepareSQL("insert into episode (idEpisode, idFile, idShow) values (NULL, %i, %i)", idFile, idShow);
1217 m_pDS->exec(strSQL.c_str());
1218 return (int)m_pDS->lastinsertid();
1222 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1227 int CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
1231 if (NULL == m_pDB.get()) return -1;
1232 if (NULL == m_pDS.get()) return -1;
1234 int idMVideo = GetMusicVideoId(strFilenameAndPath);
1237 int idFile = AddFile(strFilenameAndPath);
1240 UpdateFileDateAdded(idFile, strFilenameAndPath);
1241 CStdString strSQL=PrepareSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %i)", idFile);
1242 m_pDS->exec(strSQL.c_str());
1243 idMVideo = (int)m_pDS->lastinsertid();
1250 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1255 //********************************************************************************************************************************
1256 int CVideoDatabase::AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value)
1260 if (NULL == m_pDB.get()) return -1;
1261 if (NULL == m_pDS.get()) return -1;
1263 CStdString strSQL = PrepareSQL("select %s from %s where %s like '%s'", firstField.c_str(), table.c_str(), secondField.c_str(), value.c_str());
1264 m_pDS->query(strSQL.c_str());
1265 if (m_pDS->num_rows() == 0)
1268 // doesnt exists, add it
1269 strSQL = PrepareSQL("insert into %s (%s, %s) values(NULL, '%s')", table.c_str(), firstField.c_str(), secondField.c_str(), value.c_str());
1270 m_pDS->exec(strSQL.c_str());
1271 int id = (int)m_pDS->lastinsertid();
1276 int id = m_pDS->fv(firstField).get_asInt();
1283 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, value.c_str() );
1289 int CVideoDatabase::AddSet(const CStdString& strSet)
1294 return AddToTable("sets", "idSet", "strSet", strSet);
1297 int CVideoDatabase::AddTag(const std::string& tag)
1302 return AddToTable("tag", "idTag", "strTag", tag);
1305 int CVideoDatabase::AddGenre(const CStdString& strGenre)
1307 return AddToTable("genre", "idGenre", "strGenre", strGenre);
1310 int CVideoDatabase::AddStudio(const CStdString& strStudio)
1312 return AddToTable("studio", "idStudio", "strStudio", strStudio);
1315 //********************************************************************************************************************************
1316 int CVideoDatabase::AddCountry(const CStdString& strCountry)
1318 return AddToTable("country", "idCountry", "strCountry", strCountry);
1321 int CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& thumbURLs, const CStdString &thumb)
1325 if (NULL == m_pDB.get()) return -1;
1326 if (NULL == m_pDS.get()) return -1;
1328 CStdString strSQL=PrepareSQL("select idActor from actors where strActor like '%s'", strActor.c_str());
1329 m_pDS->query(strSQL.c_str());
1330 if (m_pDS->num_rows() == 0)
1333 // doesnt exists, add it
1334 strSQL=PrepareSQL("insert into actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),thumbURLs.c_str());
1335 m_pDS->exec(strSQL.c_str());
1336 idActor = (int)m_pDS->lastinsertid();
1340 idActor = m_pDS->fv("idActor").get_asInt();
1342 // update the thumb url's
1343 if (!thumbURLs.empty())
1345 strSQL=PrepareSQL("update actors set strThumb='%s' where idActor=%i",thumbURLs.c_str(),idActor);
1346 m_pDS->exec(strSQL.c_str());
1351 SetArtForItem(idActor, "actor", "thumb", thumb);
1356 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
1363 void CVideoDatabase::AddLinkToActor(const char *table, int actorID, const char *field, int secondID, const CStdString &role, int order)
1367 if (NULL == m_pDB.get()) return ;
1368 if (NULL == m_pDS.get()) return ;
1370 CStdString strSQL=PrepareSQL("select * from actorlink%s where idActor=%i and id%s=%i", table, actorID, field, secondID);
1371 m_pDS->query(strSQL.c_str());
1372 if (m_pDS->num_rows() == 0)
1374 // doesnt exists, add it
1375 strSQL=PrepareSQL("insert into actorlink%s (idActor, id%s, strRole, iOrder) values(%i,%i,'%s',%i)", table, field, actorID, secondID, role.c_str(), order);
1376 m_pDS->exec(strSQL.c_str());
1382 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1386 void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1390 if (NULL == m_pDB.get()) return ;
1391 if (NULL == m_pDS.get()) return ;
1393 CStdString strSQL = PrepareSQL("select * from %s where %s=%i and %s=%i", table, firstField, firstID, secondField, secondID);
1394 if (typeField != NULL && type != NULL)
1395 strSQL += PrepareSQL(" and %s='%s'", typeField, type);
1396 m_pDS->query(strSQL.c_str());
1397 if (m_pDS->num_rows() == 0)
1399 // doesnt exists, add it
1400 if (typeField == NULL || type == NULL)
1401 strSQL = PrepareSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
1403 strSQL = PrepareSQL("insert into %s (%s,%s,%s) values(%i,%i,'%s')", table, firstField, secondField, typeField, firstID, secondID, type);
1404 m_pDS->exec(strSQL.c_str());
1410 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1414 void CVideoDatabase::RemoveFromLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1418 if (NULL == m_pDB.get()) return ;
1419 if (NULL == m_pDS.get()) return ;
1421 CStdString strSQL = PrepareSQL("DELETE FROM %s WHERE %s = %i AND %s = %i", table, firstField, firstID, secondField, secondID);
1422 if (typeField != NULL && type != NULL)
1423 strSQL += PrepareSQL(" AND %s='%s'", typeField, type);
1424 m_pDS->exec(strSQL.c_str());
1428 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1433 void CVideoDatabase::AddTagToItem(int idMovie, int idTag, const std::string &type)
1438 AddToLinkTable("taglinks", "idTag", idTag, "idMedia", idMovie, "media_type", type.c_str());
1441 void CVideoDatabase::RemoveTagFromItem(int idItem, int idTag, const std::string &type)
1446 RemoveFromLinkTable("taglinks", "idTag", idTag, "idMedia", idItem, "media_type", type.c_str());
1449 void CVideoDatabase::RemoveTagsFromItem(int idItem, const std::string &type)
1454 m_pDS2->exec(PrepareSQL("DELETE FROM taglinks WHERE idMedia=%d AND media_type='%s'", idItem, type.c_str()));
1458 void CVideoDatabase::AddCast(int idMedia, const char *table, const char *field, const std::vector< SActorInfo > &cast)
1463 int order = std::max_element(cast.begin(), cast.end())->order;
1464 for (CVideoInfoTag::iCast it = cast.begin(); it != cast.end(); ++it)
1466 int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
1467 AddLinkToActor(table, idActor, field, idMedia, it->strRole, it->order >= 0 ? it->order : ++order);
1471 void CVideoDatabase::AddArtistToMusicVideo(int idMVideo, int idArtist)
1473 AddToLinkTable("artistlinkmusicvideo", "idArtist", idArtist, "idMVideo", idMVideo);
1476 //****Directors + Writers****
1477 void CVideoDatabase::AddDirectorToMovie(int idMovie, int idDirector)
1479 AddToLinkTable("directorlinkmovie", "idDirector", idDirector, "idMovie", idMovie);
1482 void CVideoDatabase::AddDirectorToTvShow(int idTvShow, int idDirector)
1484 AddToLinkTable("directorlinktvshow", "idDirector", idDirector, "idShow", idTvShow);
1487 void CVideoDatabase::AddWriterToEpisode(int idEpisode, int idWriter)
1489 AddToLinkTable("writerlinkepisode", "idWriter", idWriter, "idEpisode", idEpisode);
1492 void CVideoDatabase::AddWriterToMovie(int idMovie, int idWriter)
1494 AddToLinkTable("writerlinkmovie", "idWriter", idWriter, "idMovie", idMovie);
1497 void CVideoDatabase::AddDirectorToEpisode(int idEpisode, int idDirector)
1499 AddToLinkTable("directorlinkepisode", "idDirector", idDirector, "idEpisode", idEpisode);
1502 void CVideoDatabase::AddDirectorToMusicVideo(int idMVideo, int idDirector)
1504 AddToLinkTable("directorlinkmusicvideo", "idDirector", idDirector, "idMVideo", idMVideo);
1508 void CVideoDatabase::AddStudioToMovie(int idMovie, int idStudio)
1510 AddToLinkTable("studiolinkmovie", "idStudio", idStudio, "idMovie", idMovie);
1513 void CVideoDatabase::AddStudioToTvShow(int idTvShow, int idStudio)
1515 AddToLinkTable("studiolinktvshow", "idStudio", idStudio, "idShow", idTvShow);
1518 void CVideoDatabase::AddStudioToMusicVideo(int idMVideo, int idStudio)
1520 AddToLinkTable("studiolinkmusicvideo", "idStudio", idStudio, "idMVideo", idMVideo);
1524 void CVideoDatabase::AddGenreToMovie(int idMovie, int idGenre)
1526 AddToLinkTable("genrelinkmovie", "idGenre", idGenre, "idMovie", idMovie);
1529 void CVideoDatabase::AddGenreToTvShow(int idTvShow, int idGenre)
1531 AddToLinkTable("genrelinktvshow", "idGenre", idGenre, "idShow", idTvShow);
1534 void CVideoDatabase::AddGenreToMusicVideo(int idMVideo, int idGenre)
1536 AddToLinkTable("genrelinkmusicvideo", "idGenre", idGenre, "idMVideo", idMVideo);
1540 void CVideoDatabase::AddCountryToMovie(int idMovie, int idCountry)
1542 AddToLinkTable("countrylinkmovie", "idCountry", idCountry, "idMovie", idMovie);
1545 //********************************************************************************************************************************
1546 bool CVideoDatabase::LoadVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details)
1548 if (GetMovieInfo(strFilenameAndPath, details))
1550 if (GetEpisodeInfo(strFilenameAndPath, details))
1552 if (GetMusicVideoInfo(strFilenameAndPath, details))
1554 if (GetFileInfo(strFilenameAndPath, details))
1560 bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
1564 if (NULL == m_pDB.get()) return false;
1565 if (NULL == m_pDS.get()) return false;
1566 int idMovie = GetMovieId(strFilenameAndPath);
1567 return (idMovie > 0); // index of zero is also invalid
1571 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1576 bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
1580 if (NULL == m_pDB.get()) return false;
1581 if (NULL == m_pDS.get()) return false;
1582 int idTvShow = GetTvShowId(strPath);
1583 return (idTvShow > 0); // index of zero is also invalid
1587 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1592 bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
1596 if (NULL == m_pDB.get()) return false;
1597 if (NULL == m_pDS.get()) return false;
1598 int idEpisode = GetEpisodeId(strFilenameAndPath);
1599 return (idEpisode > 0); // index of zero is also invalid
1603 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1608 bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
1612 if (NULL == m_pDB.get()) return false;
1613 if (NULL == m_pDS.get()) return false;
1614 int idMVideo = GetMusicVideoId(strFilenameAndPath);
1615 return (idMVideo > 0); // index of zero is also invalid
1619 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1624 void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath, int idTvShow /* = -1 */)
1628 if (NULL == m_pDB.get()) return ;
1629 if (NULL == m_pDS.get()) return ;
1633 idTvShow = GetTvShowId(strPath);
1639 strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
1640 m_pDS->exec(strSQL.c_str());
1642 strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
1643 m_pDS->exec(strSQL.c_str());
1645 strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
1646 m_pDS->exec(strSQL.c_str());
1648 strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
1649 m_pDS->exec(strSQL.c_str());
1651 // remove all info other than the id
1652 // we do this due to the way we have the link between the file + movie tables.
1654 std::vector<string> ids;
1655 for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1656 ids.push_back(StringUtils::Format("c%02d=NULL", iType));
1658 strSQL = "update tvshow set ";
1659 strSQL += StringUtils::Join(ids, ", ");
1660 strSQL += PrepareSQL(" where idShow=%i", idTvShow);
1661 m_pDS->exec(strSQL.c_str());
1665 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1669 //********************************************************************************************************************************
1670 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
1673 filter.join = "LEFT JOIN actorlinkmovie ON actorlinkmovie.idMovie=movieview.idMovie "
1674 "LEFT JOIN actors a ON a.idActor=actorlinkmovie.idActor "
1675 "LEFT JOIN directorlinkmovie ON directorlinkmovie.idMovie=movieview.idMovie "
1676 "LEFT JOIN actors d ON d.idActor=directorlinkmovie.idDirector";
1677 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1678 filter.group = "movieview.idMovie";
1679 GetMoviesByWhere("videodb://movies/titles/", filter, items);
1682 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
1685 filter.join = "LEFT JOIN actorlinktvshow ON actorlinktvshow.idShow=tvshowview.idShow "
1686 "LEFT JOIN actors a ON a.idActor=actorlinktvshow.idActor "
1687 "LEFT JOIN directorlinktvshow ON directorlinktvshow.idShow=tvshowview.idShow "
1688 "LEFT JOIN actors d ON d.idActor=directorlinktvshow.idDirector";
1689 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1690 filter.group = "tvshowview.idShow";
1691 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
1694 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
1697 filter.join = "LEFT JOIN actorlinkepisode ON actorlinkepisode.idEpisode=episodeview.idEpisode "
1698 "LEFT JOIN actors a ON a.idActor=actorlinkepisode.idActor "
1699 "LEFT JOIN directorlinkepisode ON directorlinkepisode.idEpisode=episodeview.idEpisode "
1700 "LEFT JOIN actors d ON d.idActor=directorlinkepisode.idDirector";
1701 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1702 filter.group = "episodeview.idEpisode";
1703 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
1706 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1711 if (NULL == m_pDB.get()) return ;
1712 if (NULL == m_pDS.get()) return ;
1715 if (strArtist.empty()) // TODO: SMARTPLAYLISTS what is this here for???
1716 strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1718 strSQL=PrepareSQL("select * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist where actors.strActor='%s'", strArtist.c_str());
1719 m_pDS->query( strSQL.c_str() );
1721 while (!m_pDS->eof())
1723 CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1724 CFileItemPtr pItem(new CFileItem(tag));
1725 pItem->SetLabel(StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator));
1733 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1737 //********************************************************************************************************************************
1738 bool CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
1742 // TODO: Optimize this - no need for all the queries!
1744 idMovie = GetMovieId(strFilenameAndPath);
1745 if (idMovie < 0) return false;
1747 CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
1748 if (!m_pDS->query(sql.c_str()))
1750 details = GetDetailsForMovie(m_pDS, true);
1751 return !details.IsEmpty();
1755 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1760 //********************************************************************************************************************************
1761 bool CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
1766 idTvShow = GetTvShowId(strPath);
1767 if (idTvShow < 0) return false;
1769 CStdString sql = PrepareSQL("SELECT * FROM tvshowview WHERE idShow=%i", idTvShow);
1770 if (!m_pDS->query(sql.c_str()))
1772 details = GetDetailsForTvShow(m_pDS, true);
1773 return !details.IsEmpty();
1777 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1782 bool CVideoDatabase::GetSeasonInfo(int idSeason, CVideoInfoTag& details)
1789 if (!m_pDB.get() || !m_pDS.get())
1792 CStdString sql = PrepareSQL("SELECT idShow FROM seasons WHERE idSeason=%i", idSeason);
1793 if (!m_pDS->query(sql.c_str()))
1797 if (m_pDS->num_rows() == 1)
1798 idShow = m_pDS->fv(0).get_asInt();
1805 CFileItemList seasons;
1806 if (!GetSeasonsNav(StringUtils::Format("videodb://tvshows/titles/%ld/", idShow), seasons, -1, -1, -1, -1, idShow, false) || seasons.Size() <= 0)
1809 for (int index = 0; index < seasons.Size(); index++)
1811 const CFileItemPtr season = seasons.Get(index);
1812 if (season->HasVideoInfoTag() && season->GetVideoInfoTag()->m_iDbId == idSeason && season->GetVideoInfoTag()->m_iIdShow == idShow)
1814 details = *season->GetVideoInfoTag();
1821 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
1826 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
1830 // TODO: Optimize this - no need for all the queries!
1832 idEpisode = GetEpisodeId(strFilenameAndPath);
1833 if (idEpisode < 0) return false;
1835 CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
1836 if (!m_pDS->query(sql.c_str()))
1838 details = GetDetailsForEpisode(m_pDS, true);
1839 return !details.IsEmpty();
1843 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1848 bool CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
1852 // TODO: Optimize this - no need for all the queries!
1854 idMVideo = GetMusicVideoId(strFilenameAndPath);
1855 if (idMVideo < 0) return false;
1857 CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
1858 if (!m_pDS->query(sql.c_str()))
1860 details = GetDetailsForMusicVideo(m_pDS, true);
1861 return !details.IsEmpty();
1865 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1870 bool CVideoDatabase::GetSetInfo(int idSet, CVideoInfoTag& details)
1878 filter.where = PrepareSQL("sets.idSet=%d", idSet);
1879 CFileItemList items;
1880 if (!GetSetsByWhere("videodb://movies/sets/", filter, items) ||
1881 items.Size() != 1 ||
1882 !items[0]->HasVideoInfoTag())
1885 details = *(items[0]->GetVideoInfoTag());
1886 return !details.IsEmpty();
1890 CLog::Log(LOGERROR, "%s (%d) failed", __FUNCTION__, idSet);
1895 bool CVideoDatabase::GetFileInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idFile /* = -1 */)
1900 idFile = GetFileId(strFilenameAndPath);
1904 CStdString sql = PrepareSQL("SELECT * FROM files "
1905 "JOIN path ON path.idPath = files.idPath "
1906 "LEFT JOIN bookmark ON bookmark.idFile = files.idFile AND bookmark.type = %i "
1907 "WHERE files.idFile = %i", CBookmark::RESUME, idFile);
1908 if (!m_pDS->query(sql.c_str()))
1911 details.m_iFileId = m_pDS->fv("files.idFile").get_asInt();
1912 details.m_strPath = m_pDS->fv("path.strPath").get_asString();
1913 CStdString strFileName = m_pDS->fv("files.strFilename").get_asString();
1914 ConstructPath(details.m_strFileNameAndPath, details.m_strPath, strFileName);
1915 details.m_playCount = max(details.m_playCount, m_pDS->fv("files.playCount").get_asInt());
1916 if (!details.m_lastPlayed.IsValid())
1917 details.m_lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
1918 if (!details.m_dateAdded.IsValid())
1919 details.m_dateAdded.SetFromDBDateTime(m_pDS->fv("files.dateAdded").get_asString());
1920 if (!details.m_resumePoint.IsSet())
1922 details.m_resumePoint.timeInSeconds = m_pDS->fv("bookmark.timeInSeconds").get_asInt();
1923 details.m_resumePoint.totalTimeInSeconds = m_pDS->fv("bookmark.totalTimeInSeconds").get_asInt();
1924 details.m_resumePoint.type = CBookmark::RESUME;
1927 return !details.IsEmpty();
1931 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1936 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1938 // add all directors
1939 for (unsigned int i = 0; i < details.m_director.size(); i++)
1940 vecDirectors.push_back(AddActor(details.m_director[i],""));
1943 for (unsigned int i = 0; i < details.m_genre.size(); i++)
1944 vecGenres.push_back(AddGenre(details.m_genre[i]));
1946 for (unsigned int i = 0; i < details.m_studio.size(); i++)
1947 vecStudios.push_back(AddStudio(details.m_studio[i]));
1950 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1952 std::vector<std::string> conditions;
1953 for (int i = min + 1; i < max; ++i)
1955 switch (offsets[i].type)
1957 case VIDEODB_TYPE_STRING:
1958 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str()));
1960 case VIDEODB_TYPE_INT:
1961 conditions.push_back(PrepareSQL("c%02d='%i'", i, *(int*)(((char*)&details)+offsets[i].offset)));
1963 case VIDEODB_TYPE_COUNT:
1965 int value = *(int*)(((char*)&details)+offsets[i].offset);
1967 conditions.push_back(PrepareSQL("c%02d=%i", i, value));
1969 conditions.push_back(PrepareSQL("c%02d=NULL", i));
1972 case VIDEODB_TYPE_BOOL:
1973 conditions.push_back(PrepareSQL("c%02d='%s'", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false"));
1975 case VIDEODB_TYPE_FLOAT:
1976 conditions.push_back(PrepareSQL("c%02d='%f'", i, *(float*)(((char*)&details)+offsets[i].offset)));
1978 case VIDEODB_TYPE_STRINGARRAY:
1979 conditions.push_back(PrepareSQL("c%02d='%s'", i, StringUtils::Join(*((std::vector<std::string>*)(((char*)&details)+offsets[i].offset)),
1980 g_advancedSettings.m_videoItemSeparator).c_str()));
1982 case VIDEODB_TYPE_DATE:
1983 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDate().c_str()));
1985 case VIDEODB_TYPE_DATETIME:
1986 conditions.push_back(PrepareSQL("c%02d='%s'", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDateTime().c_str()));
1990 return StringUtils::Join(conditions, ",");
1993 //********************************************************************************************************************************
1994 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMovie /* = -1 */)
2001 idMovie = GetMovieId(strFilenameAndPath);
2004 DeleteMovie(strFilenameAndPath, true, idMovie); // true to keep the table entry
2007 // only add a new movie if we don't already have a valid idMovie
2008 // (DeleteMovie is called with bKeepId == true so the movie won't
2009 // be removed from the movie table)
2010 idMovie = AddMovie(strFilenameAndPath);
2013 RollbackTransaction();
2018 vector<int> vecDirectors;
2019 vector<int> vecGenres;
2020 vector<int> vecStudios;
2021 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2023 for (unsigned int i = 0; i < vecGenres.size(); ++i)
2024 AddGenreToMovie(idMovie, vecGenres[i]);
2026 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2027 AddDirectorToMovie(idMovie, vecDirectors[i]);
2029 for (unsigned int i = 0; i < vecStudios.size(); ++i)
2030 AddStudioToMovie(idMovie, vecStudios[i]);
2033 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2034 AddWriterToMovie(idMovie, AddActor(details.m_writingCredits[i],""));
2036 AddCast(idMovie, "movie", "movie", details.m_cast);
2040 if (!details.m_strSet.empty())
2042 idSet = AddSet(details.m_strSet);
2043 // add art if not available
2044 map<string, string> setArt;
2045 if (!GetArtForItem(idSet, "set", setArt))
2046 SetArtForItem(idSet, "set", artwork);
2050 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2052 int idTag = AddTag(details.m_tags[i]);
2053 AddTagToItem(idMovie, idTag, "movie");
2057 for (unsigned int i = 0; i < details.m_country.size(); i++)
2058 AddCountryToMovie(idMovie, AddCountry(details.m_country[i]));
2060 if (details.HasStreamDetails())
2061 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2063 SetArtForItem(idMovie, "movie", artwork);
2065 // query DB for any movies matching imdbid and year
2066 CStdString strSQL = PrepareSQL("select files.playCount, files.lastPlayed from movie,files where files.idFile=movie.idFile and movie.c%02d='%s' and movie.c%02d=%i and movie.idMovie!=%i and files.playCount > 0", VIDEODB_ID_IDENT, details.m_strIMDBNumber.c_str(), VIDEODB_ID_YEAR, details.m_iYear, idMovie);
2067 m_pDS->query(strSQL.c_str());
2071 int playCount = m_pDS->fv("files.playCount").get_asInt();
2073 CDateTime lastPlayed;
2074 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2076 int idFile = GetFileId(strFilenameAndPath);
2078 // update with playCount and lastPlayed
2079 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2080 m_pDS->exec(strSQL.c_str());
2085 // update our movie table (we know it was added already above)
2086 // and insert the new row
2087 CStdString sql = "update movie set " + GetValueString(details, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
2089 sql += PrepareSQL(", idSet = %i", idSet);
2091 sql += ", idSet = NULL";
2092 sql += PrepareSQL(" where idMovie=%i", idMovie);
2093 m_pDS->exec(sql.c_str());
2094 CommitTransaction();
2100 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2102 RollbackTransaction();
2106 int CVideoDatabase::SetDetailsForMovieSet(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idSet /* = -1 */)
2108 if (details.m_strTitle.empty())
2116 idSet = AddSet(details.m_strTitle);
2119 RollbackTransaction();
2124 SetArtForItem(idSet, "set", artwork);
2126 // and insert the new row
2127 CStdString sql = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", details.m_strTitle.c_str(), idSet);
2128 m_pDS->exec(sql.c_str());
2129 CommitTransaction();
2135 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
2137 RollbackTransaction();
2141 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, map<string, string> > &seasonArt, int idTvShow /*= -1 */)
2145 if (!m_pDB.get() || !m_pDS.get())
2147 CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
2154 idTvShow = GetTvShowId(strPath);
2157 DeleteDetailsForTvShow(strPath, idTvShow);
2160 idTvShow = AddTvShow(strPath);
2163 RollbackTransaction();
2168 vector<int> vecDirectors;
2169 vector<int> vecGenres;
2170 vector<int> vecStudios;
2171 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2173 AddCast(idTvShow, "tvshow", "show", details.m_cast);
2176 for (i = 0; i < vecGenres.size(); ++i)
2177 AddGenreToTvShow(idTvShow, vecGenres[i]);
2179 for (i = 0; i < vecDirectors.size(); ++i)
2180 AddDirectorToTvShow(idTvShow, vecDirectors[i]);
2182 for (i = 0; i < vecStudios.size(); ++i)
2183 AddStudioToTvShow(idTvShow, vecStudios[i]);
2186 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2188 int idTag = AddTag(details.m_tags[i]);
2189 AddTagToItem(idTvShow, idTag, "tvshow");
2192 // add "all seasons" - the rest are added in SetDetailsForEpisode
2193 AddSeason(idTvShow, -1);
2195 SetArtForItem(idTvShow, "tvshow", artwork);
2196 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
2198 int idSeason = AddSeason(idTvShow, i->first);
2200 SetArtForItem(idSeason, "season", i->second);
2203 // and insert the new row
2204 CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
2205 sql += PrepareSQL(" where idShow=%i", idTvShow);
2206 m_pDS->exec(sql.c_str());
2208 CommitTransaction();
2214 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2216 RollbackTransaction();
2220 int CVideoDatabase::SetDetailsForSeason(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idShow, int idSeason /* = -1 */)
2222 if (idShow < 0 || details.m_iSeason < 0)
2230 idSeason = AddSeason(idShow, details.m_iSeason);
2233 RollbackTransaction();
2238 SetArtForItem(idSeason, "season", artwork);
2240 // and insert the new row
2241 CStdString sql = PrepareSQL("UPDATE seasons SET season=%i WHERE idSeason=%i", details.m_iSeason, idSeason);
2242 m_pDS->exec(sql.c_str());
2243 CommitTransaction();
2249 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
2251 RollbackTransaction();
2255 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idShow, int idEpisode)
2261 idEpisode = GetEpisodeId(strFilenameAndPath);
2264 DeleteEpisode(strFilenameAndPath, idEpisode, true); // true to keep the table entry
2267 // only add a new episode if we don't already have a valid idEpisode
2268 // (DeleteEpisode is called with bKeepId == true so the episode won't
2269 // be removed from the episode table)
2270 idEpisode = AddEpisode(idShow,strFilenameAndPath);
2273 RollbackTransaction();
2278 vector<int> vecDirectors;
2279 vector<int> vecGenres;
2280 vector<int> vecStudios;
2281 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2283 AddCast(idEpisode, "episode", "episode", details.m_cast);
2286 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2287 AddWriterToEpisode(idEpisode, AddActor(details.m_writingCredits[i],""));
2289 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2291 AddDirectorToEpisode(idEpisode, vecDirectors[i]);
2294 if (details.HasStreamDetails())
2296 if (details.m_iFileId != -1)
2297 SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2299 SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
2302 // ensure we have this season already added
2303 AddSeason(idShow, details.m_iSeason);
2305 SetArtForItem(idEpisode, "episode", artwork);
2307 // query DB for any episodes matching idShow, Season and Episode
2308 CStdString strSQL = PrepareSQL("select files.playCount, files.lastPlayed from episode, files where files.idFile=episode.idFile and episode.c%02d=%i and episode.c%02d=%i AND episode.idShow=%i and episode.idEpisode!=%i and files.playCount > 0",VIDEODB_ID_EPISODE_SEASON, details.m_iSeason, VIDEODB_ID_EPISODE_EPISODE, details.m_iEpisode, idShow, idEpisode);
2309 m_pDS->query(strSQL.c_str());
2313 int playCount = m_pDS->fv("files.playCount").get_asInt();
2315 CDateTime lastPlayed;
2316 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2318 int idFile = GetFileId(strFilenameAndPath);
2320 // update with playCount and lastPlayed
2321 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2322 m_pDS->exec(strSQL.c_str());
2327 // and insert the new row
2328 CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
2329 sql += PrepareSQL(" where idEpisode=%i", idEpisode);
2330 m_pDS->exec(sql.c_str());
2331 CommitTransaction();
2337 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2339 RollbackTransaction();
2343 int CVideoDatabase::GetSeasonId(int showID, int season)
2345 CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
2346 CStdString id = GetSingleValue("seasons", "idSeason", sql);
2349 return strtol(id.c_str(), NULL, 10);
2352 int CVideoDatabase::AddSeason(int showID, int season)
2354 int seasonId = GetSeasonId(showID, season);
2357 if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
2358 seasonId = (int)m_pDS->lastinsertid();
2363 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
2370 idMVideo = GetMusicVideoId(strFilenameAndPath);
2373 DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
2376 // only add a new musicvideo if we don't already have a valid idMVideo
2377 // (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
2378 // be removed from the musicvideo table)
2379 idMVideo = AddMusicVideo(strFilenameAndPath);
2382 RollbackTransaction();
2387 vector<int> vecDirectors;
2388 vector<int> vecGenres;
2389 vector<int> vecStudios;
2390 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2393 if (!details.m_artist.empty())
2395 for (unsigned int i = 0; i < details.m_artist.size(); i++)
2397 CStdString artist = details.m_artist[i];
2398 StringUtils::Trim(artist);
2399 int idArtist = AddActor(artist,"");
2400 AddArtistToMusicVideo(idMVideo, idArtist);
2405 for (i = 0; i < vecGenres.size(); ++i)
2407 AddGenreToMusicVideo(idMVideo, vecGenres[i]);
2410 for (i = 0; i < vecDirectors.size(); ++i)
2412 AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
2415 for (i = 0; i < vecStudios.size(); ++i)
2417 AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2421 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2423 int idTag = AddTag(details.m_tags[i]);
2424 AddTagToItem(idMVideo, idTag, "musicvideo");
2427 if (details.HasStreamDetails())
2428 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2430 SetArtForItem(idMVideo, "musicvideo", artwork);
2432 // update our movie table (we know it was added already above)
2433 // and insert the new row
2434 CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
2435 sql += PrepareSQL(" where idMVideo=%i", idMVideo);
2436 m_pDS->exec(sql.c_str());
2437 CommitTransaction();
2443 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2445 RollbackTransaction();
2449 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2451 // AddFile checks to make sure the file isn't already in the DB first
2452 int idFile = AddFile(strFileNameAndPath);
2455 SetStreamDetailsForFileId(details, idFile);
2458 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2466 m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2468 for (int i=1; i<=details.GetVideoStreamCount(); i++)
2470 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2471 "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration, strStereoMode) "
2472 "VALUES (%i,%i,'%s',%f,%i,%i,%i,'%s')",
2473 idFile, (int)CStreamDetail::VIDEO,
2474 details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2475 details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i),
2476 details.GetStereoMode(i).c_str()));
2478 for (int i=1; i<=details.GetAudioStreamCount(); i++)
2480 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2481 "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2482 "VALUES (%i,%i,'%s',%i,'%s')",
2483 idFile, (int)CStreamDetail::AUDIO,
2484 details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2485 details.GetAudioLanguage(i).c_str()));
2487 for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2489 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2490 "(idFile, iStreamType, strSubtitleLanguage) "
2491 "VALUES (%i,%i,'%s')",
2492 idFile, (int)CStreamDetail::SUBTITLE,
2493 details.GetSubtitleLanguage(i).c_str()));
2496 // update the runtime information, if empty
2497 if (details.GetVideoDuration())
2499 vector< pair<string, int> > tables;
2500 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
2501 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
2502 tables.push_back(make_pair("musicvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
2503 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
2505 CStdString sql = PrepareSQL("update %s set c%02d=%d where idFile=%d and c%02d=''",
2506 i->first.c_str(), i->second, details.GetVideoDuration(), idFile, i->second);
2511 CommitTransaction();
2515 RollbackTransaction();
2516 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2520 //********************************************************************************************************************************
2521 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2525 if (NULL == m_pDB.get()) return ;
2526 if (NULL == m_pDS.get()) return ;
2528 if (idMovie < 0) return ;
2531 if (iType == VIDEODB_CONTENT_MOVIES)
2532 strSQL=PrepareSQL("select path.strPath,files.strFileName from path, files, movie where path.idPath=files.idPath and files.idFile=movie.idFile and movie.idMovie=%i order by strFilename", idMovie );
2533 if (iType == VIDEODB_CONTENT_EPISODES)
2534 strSQL=PrepareSQL("select path.strPath,files.strFileName from path, files, episode where path.idPath=files.idPath and files.idFile=episode.idFile and episode.idEpisode=%i order by strFilename", idMovie );
2535 if (iType == VIDEODB_CONTENT_TVSHOWS)
2536 strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2537 if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2538 strSQL=PrepareSQL("select path.strPath,files.strFileName from path, files, musicvideo where path.idPath=files.idPath and files.idFile=musicvideo.idFile and musicvideo.idMVideo=%i order by strFilename", idMovie );
2540 m_pDS->query( strSQL.c_str() );
2543 if (iType != VIDEODB_CONTENT_TVSHOWS)
2545 CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2546 ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2549 filePath = m_pDS->fv("path.strPath").get_asString();
2555 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2559 //********************************************************************************************************************************
2560 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend, long partNumber)
2564 if (URIUtils::IsStack(strFilenameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(strFilenameAndPath),false).IsDVDImage())
2566 CStackDirectory dir;
2567 CFileItemList fileList;
2568 dir.GetDirectory(strFilenameAndPath, fileList);
2571 for (int i = fileList.Size() - 1; i >= 0; i--) // put the bookmarks of the highest part first in the list
2572 GetBookMarksForFile(fileList[i]->GetPath(), bookmarks, type, true, (i+1));
2576 int idFile = GetFileId(strFilenameAndPath);
2577 if (idFile < 0) return ;
2579 bookmarks.erase(bookmarks.begin(), bookmarks.end());
2580 if (NULL == m_pDB.get()) return ;
2581 if (NULL == m_pDS.get()) return ;
2583 CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2584 m_pDS->query( strSQL.c_str() );
2585 while (!m_pDS->eof())
2588 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2589 bookmark.partNumber = partNumber;
2590 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2591 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2592 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2593 bookmark.player = m_pDS->fv("player").get_asString();
2594 bookmark.type = type;
2595 if (type == CBookmark::EPISODE)
2597 CStdString strSQL2=PrepareSQL("select c%02d, c%02d from episode where c%02d=%i order by c%02d, c%02d", VIDEODB_ID_EPISODE_EPISODE, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_BOOKMARK, m_pDS->fv("idBookmark").get_asInt(), VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTEPISODE);
2598 m_pDS2->query(strSQL2.c_str());
2599 bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2600 bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2603 bookmarks.push_back(bookmark);
2606 //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2612 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2616 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2618 VECBOOKMARKS bookmarks;
2619 GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2620 if (bookmarks.size() > 0)
2622 bookmark = bookmarks[0];
2628 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2630 if (!m_pDB.get() || !m_pDS.get())
2633 int fileID = GetFileId(strFilenameAndPath);
2639 CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2640 m_pDS->exec(sql.c_str());
2644 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2648 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2652 CStdString strSQL = PrepareSQL("select * from episodeview where idFile=%i order by c%02d, c%02d asc", GetFileId(strFilenameAndPath), VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTEPISODE);
2653 m_pDS->query(strSQL.c_str());
2654 while (!m_pDS->eof())
2656 episodes.push_back(GetDetailsForEpisode(m_pDS));
2663 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2667 //********************************************************************************************************************************
2668 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2672 int idFile = AddFile(strFilenameAndPath);
2675 if (NULL == m_pDB.get()) return ;
2676 if (NULL == m_pDS.get()) return ;
2680 if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2682 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2684 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
2686 /* get a bookmark within the same time as previous */
2687 double mintime = bookmark.timeInSeconds - 0.5f;
2688 double maxtime = bookmark.timeInSeconds + 0.5f;
2689 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=%i and (timeInSeconds between %f and %f) and playerState='%s'", idFile, (int)type, mintime, maxtime, bookmark.playerState.c_str());
2692 if (type != CBookmark::EPISODE)
2695 m_pDS->query( strSQL.c_str() );
2696 if (m_pDS->num_rows() != 0)
2697 idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2700 // update or insert depending if it existed before
2701 if (idBookmark >= 0 )
2702 strSQL=PrepareSQL("update bookmark set timeInSeconds = %f, totalTimeInSeconds = %f, thumbNailImage = '%s', player = '%s', playerState = '%s' where idBookmark = %i", bookmark.timeInSeconds, bookmark.totalTimeInSeconds, bookmark.thumbNailImage.c_str(), bookmark.player.c_str(), bookmark.playerState.c_str(), idBookmark);
2704 strSQL=PrepareSQL("insert into bookmark (idBookmark, idFile, timeInSeconds, totalTimeInSeconds, thumbNailImage, player, playerState, type) values(NULL,%i,%f,%f,'%s','%s','%s', %i)", idFile, bookmark.timeInSeconds, bookmark.totalTimeInSeconds, bookmark.thumbNailImage.c_str(), bookmark.player.c_str(), bookmark.playerState.c_str(), (int)type);
2706 m_pDS->exec(strSQL.c_str());
2710 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2714 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2718 int idFile = GetFileId(strFilenameAndPath);
2719 if (idFile < 0) return ;
2720 if (NULL == m_pDB.get()) return ;
2721 if (NULL == m_pDS.get()) return ;
2723 /* a litle bit uggly, we clear first bookmark that is within one second of given */
2724 /* should be no problem since we never add bookmarks that are closer than that */
2725 double mintime = bookmark.timeInSeconds - 0.5f;
2726 double maxtime = bookmark.timeInSeconds + 0.5f;
2727 CStdString strSQL = PrepareSQL("select idBookmark from bookmark where idFile=%i and type=%i and playerState like '%s' and player like '%s' and (timeInSeconds between %f and %f)", idFile, type, bookmark.playerState.c_str(), bookmark.player.c_str(), mintime, maxtime);
2729 m_pDS->query( strSQL.c_str() );
2730 if (m_pDS->num_rows() != 0)
2732 int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2733 strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2734 m_pDS->exec(strSQL.c_str());
2735 if (type == CBookmark::EPISODE)
2737 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i and c%02d=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile, VIDEODB_ID_EPISODE_BOOKMARK, idBookmark);
2738 m_pDS->exec(strSQL.c_str());
2746 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2750 //********************************************************************************************************************************
2751 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2755 int idFile = GetFileId(strFilenameAndPath);
2756 if (idFile < 0) return ;
2757 if (NULL == m_pDB.get()) return ;
2758 if (NULL == m_pDS.get()) return ;
2760 CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2761 m_pDS->exec(strSQL.c_str());
2762 if (type == CBookmark::EPISODE)
2764 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2765 m_pDS->exec(strSQL.c_str());
2770 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2775 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2779 CStdString strSQL = PrepareSQL("select bookmark.* from bookmark join episode on episode.c%02d=bookmark.idBookmark where episode.idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2780 m_pDS->query( strSQL.c_str() );
2783 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2784 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2785 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2786 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2787 bookmark.player = m_pDS->fv("player").get_asString();
2788 bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2799 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2805 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2809 int idFile = GetFileId(tag.m_strFileNameAndPath);
2810 // delete the current episode for the selected episode number
2811 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where c%02d=%i and c%02d=%i and idFile=%i)", VIDEODB_ID_EPISODE_BOOKMARK, VIDEODB_ID_EPISODE_SEASON, tag.m_iSeason, VIDEODB_ID_EPISODE_EPISODE, tag.m_iEpisode, idFile);
2812 m_pDS->exec(strSQL.c_str());
2814 AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2815 int idBookmark = (int)m_pDS->lastinsertid();
2816 strSQL = PrepareSQL("update episode set c%02d=%i where c%02d=%i and c%02d=%i and idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idBookmark, VIDEODB_ID_EPISODE_SEASON, tag.m_iSeason, VIDEODB_ID_EPISODE_EPISODE, tag.m_iEpisode, idFile);
2817 m_pDS->exec(strSQL.c_str());
2821 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2825 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2829 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2830 m_pDS->exec(strSQL.c_str());
2831 strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2832 m_pDS->exec(strSQL.c_str());
2836 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2840 //********************************************************************************************************************************
2841 void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
2847 GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
2849 DeleteMovie(path, bKeepId, idMovie);
2852 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
2856 if (NULL == m_pDB.get()) return ;
2857 if (NULL == m_pDS.get()) return ;
2860 idMovie = GetMovieId(strFilenameAndPath);
2868 strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2869 m_pDS->exec(strSQL.c_str());
2871 strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2872 m_pDS->exec(strSQL.c_str());
2874 strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2875 m_pDS->exec(strSQL.c_str());
2877 strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2878 m_pDS->exec(strSQL.c_str());
2880 strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2881 m_pDS->exec(strSQL.c_str());
2883 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2885 // keep the movie table entry, linking to tv shows, and bookmarks
2886 // so we can update the data in place
2887 // the ancilliary tables are still purged
2890 ClearBookMarksOfFile(strFilenameAndPath);
2892 strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2893 m_pDS->exec(strSQL.c_str());
2895 strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2896 m_pDS->exec(strSQL.c_str());
2899 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2901 AnnounceRemove("movie", idMovie);
2903 CStdString strPath, strFileName;
2904 SplitPath(strFilenameAndPath,strPath,strFileName);
2905 InvalidatePathHash(strPath);
2906 CommitTransaction();
2911 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2912 RollbackTransaction();
2916 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2922 GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2924 DeleteTvShow(path, bKeepId, idTvShow);
2927 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2931 if (NULL == m_pDB.get()) return ;
2932 if (NULL == m_pDS.get()) return ;
2935 idTvShow = GetTvShowId(strPath);
2942 CStdString strSQL=PrepareSQL("select episode.idEpisode,path.strPath,files.strFileName from episode,path,files where episode.idShow=%i and episode.idFile=files.idFile and files.idPath=path.idPath",idTvShow);
2943 m_pDS2->query(strSQL.c_str());
2944 while (!m_pDS2->eof())
2946 CStdString strFilenameAndPath;
2947 CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2948 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2949 ConstructPath(strFilenameAndPath, strPath, strFileName);
2950 DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2954 DeleteDetailsForTvShow(strPath, idTvShow);
2956 strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2957 m_pDS->exec(strSQL.c_str());
2959 // keep tvshow table and movielink table so we can update data in place
2962 strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2963 m_pDS->exec(strSQL.c_str());
2965 strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2966 m_pDS->exec(strSQL.c_str());
2968 strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2969 m_pDS->exec(strSQL.c_str());
2972 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2974 AnnounceRemove("tvshow", idTvShow);
2976 InvalidatePathHash(strPath);
2978 CommitTransaction();
2983 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2984 RollbackTransaction();
2988 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2994 GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2996 DeleteEpisode(path, idEpisode, bKeepId);
2999 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
3003 if (NULL == m_pDB.get()) return ;
3004 if (NULL == m_pDS.get()) return ;
3007 idEpisode = GetEpisodeId(strFilenameAndPath);
3014 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3016 AnnounceRemove("episode", idEpisode);
3019 strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
3020 m_pDS->exec(strSQL.c_str());
3022 strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
3023 m_pDS->exec(strSQL.c_str());
3025 strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
3026 m_pDS->exec(strSQL.c_str());
3028 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3030 // keep episode table entry and bookmarks so we can update the data in place
3031 // the ancilliary tables are still purged
3034 ClearBookMarksOfFile(strFilenameAndPath);
3036 strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
3037 m_pDS->exec(strSQL.c_str());
3043 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3047 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
3049 if (idMusicVideo < 0)
3053 GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
3055 DeleteMusicVideo(path, bKeepId, idMusicVideo);
3058 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
3062 if (NULL == m_pDB.get()) return ;
3063 if (NULL == m_pDS.get()) return ;
3066 idMVideo = GetMusicVideoId(strFilenameAndPath);
3074 strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
3075 m_pDS->exec(strSQL.c_str());
3077 strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
3078 m_pDS->exec(strSQL.c_str());
3080 strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
3081 m_pDS->exec(strSQL.c_str());
3083 strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
3084 m_pDS->exec(strSQL.c_str());
3086 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3088 // keep the music video table entry and bookmarks so we can update data in place
3089 // the ancilliary tables are still purged
3092 ClearBookMarksOfFile(strFilenameAndPath);
3094 strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
3095 m_pDS->exec(strSQL.c_str());
3098 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3100 AnnounceRemove("musicvideo", idMVideo);
3102 CStdString strPath, strFileName;
3103 SplitPath(strFilenameAndPath,strPath,strFileName);
3104 InvalidatePathHash(strPath);
3105 CommitTransaction();
3110 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3111 RollbackTransaction();
3115 void CVideoDatabase::DeleteStreamDetails(int idFile)
3117 m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
3120 void CVideoDatabase::DeleteSet(int idSet)
3124 if (NULL == m_pDB.get()) return ;
3125 if (NULL == m_pDS.get()) return ;
3128 strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
3129 m_pDS->exec(strSQL.c_str());
3130 strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3131 m_pDS->exec(strSQL.c_str());
3135 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3139 void CVideoDatabase::ClearMovieSet(int idMovie)
3141 SetMovieSet(idMovie, -1);
3144 void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
3147 ExecuteQuery(PrepareSQL("update movie set idSet = %i where idMovie = %i", idSet, idMovie));
3149 ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
3152 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3156 if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3160 if (mediaType == VIDEODB_CONTENT_MOVIES)
3162 else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3164 else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3165 type = "musicvideo";
3170 strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3171 m_pDS->exec(strSQL.c_str());
3175 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3179 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3181 GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3184 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3186 for (int i = min + 1; i < max; i++)
3188 switch (offsets[i].type)
3190 case VIDEODB_TYPE_STRING:
3191 *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3193 case VIDEODB_TYPE_INT:
3194 case VIDEODB_TYPE_COUNT:
3195 *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3197 case VIDEODB_TYPE_BOOL:
3198 *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3200 case VIDEODB_TYPE_FLOAT:
3201 *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3203 case VIDEODB_TYPE_STRINGARRAY:
3204 *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3206 case VIDEODB_TYPE_DATE:
3207 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3209 case VIDEODB_TYPE_DATETIME:
3210 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3216 DWORD movieTime = 0;
3219 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3221 CVideoInfoTag details;
3226 case VIDEODB_CONTENT_MOVIES:
3227 GetMovieInfo("", details, id);
3229 case VIDEODB_CONTENT_TVSHOWS:
3230 GetTvShowInfo("", details, id);
3232 case VIDEODB_CONTENT_EPISODES:
3233 GetEpisodeInfo("", details, id);
3235 case VIDEODB_CONTENT_MUSICVIDEOS:
3236 GetMusicVideoInfo("", details, id);
3245 bool CVideoDatabase::GetStreamDetails(CFileItem& item)
3247 // Note that this function (possibly) creates VideoInfoTags for items that don't have one yet!
3250 if (item.HasVideoInfoTag())
3251 fileId = item.GetVideoInfoTag()->m_iFileId;
3254 fileId = GetFileId(item);
3259 // Have a file id, get stream details if available (creates tag either way)
3260 item.GetVideoInfoTag()->m_iFileId = fileId;
3261 return GetStreamDetails(*item.GetVideoInfoTag());
3264 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3266 if (tag.m_iFileId < 0)
3269 bool retVal = false;
3271 CStreamDetails& details = tag.m_streamDetails;
3274 auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3277 CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3282 CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3285 case CStreamDetail::VIDEO:
3287 CStreamDetailVideo *p = new CStreamDetailVideo();
3288 p->m_strCodec = pDS->fv(2).get_asString();
3289 p->m_fAspect = pDS->fv(3).get_asFloat();
3290 p->m_iWidth = pDS->fv(4).get_asInt();
3291 p->m_iHeight = pDS->fv(5).get_asInt();
3292 p->m_iDuration = pDS->fv(10).get_asInt();
3293 p->m_strStereoMode = pDS->fv(11).get_asString();
3294 details.AddStream(p);
3298 case CStreamDetail::AUDIO:
3300 CStreamDetailAudio *p = new CStreamDetailAudio();
3301 p->m_strCodec = pDS->fv(6).get_asString();
3302 if (pDS->fv(7).get_isNull())
3303 p->m_iChannels = -1;
3305 p->m_iChannels = pDS->fv(7).get_asInt();
3306 p->m_strLanguage = pDS->fv(8).get_asString();
3307 details.AddStream(p);
3311 case CStreamDetail::SUBTITLE:
3313 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3314 p->m_strLanguage = pDS->fv(9).get_asString();
3315 details.AddStream(p);
3328 CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3330 details.DetermineBestStreams();
3332 if (details.GetVideoDuration() > 0)
3333 tag.m_duration = details.GetVideoDuration();
3338 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3340 if (tag.m_iFileId < 0)
3347 if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3349 CStackDirectory dir;
3350 CFileItemList fileList;
3351 dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3352 tag.m_resumePoint.Reset();
3353 for (int i = fileList.Size() - 1; i >= 0; i--)
3356 if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3358 tag.m_resumePoint = bookmark;
3359 tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3367 CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3368 m_pDS2->query( strSQL.c_str() );
3371 tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3372 tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3373 tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3374 tag.m_resumePoint.type = CBookmark::RESUME;
3382 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3388 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3390 return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3393 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3395 CVideoInfoTag details;
3400 DWORD time = XbmcThreads::SystemClockMillis();
3401 int idMovie = record->at(0).get_asInt();
3403 GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3405 details.m_iDbId = idMovie;
3406 details.m_type = "movie";
3408 details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3409 details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3410 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3411 details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3412 CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3413 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3414 details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3415 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3416 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3417 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3418 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3419 details.m_resumePoint.type = CBookmark::RESUME;
3421 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3425 GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3427 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3428 details.m_strPictureURL.Parse();
3431 CStdString strSQL = PrepareSQL("SELECT tag.strTag FROM tag, taglinks WHERE taglinks.idMedia = %i AND taglinks.media_type = 'movie' AND taglinks.idTag = tag.idTag ORDER BY tag.idTag", idMovie);
3432 m_pDS2->query(strSQL.c_str());
3433 while (!m_pDS2->eof())
3435 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3439 // create tvshowlink string
3441 GetLinksToTvShow(idMovie,links);
3442 for (unsigned int i=0;i<links.size();++i)
3444 CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3445 VIDEODB_ID_TV_TITLE,links[i]);
3446 m_pDS2->query(strSQL.c_str());
3448 details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3452 // get streamdetails
3453 GetStreamDetails(details);
3458 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3460 return GetDetailsForTvShow(pDS->get_sql_record(), getDetails, item);
3463 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */, CFileItem* item /* = NULL */)
3465 CVideoInfoTag details;
3470 DWORD time = XbmcThreads::SystemClockMillis();
3471 int idTvShow = record->at(0).get_asInt();
3473 GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3474 details.m_iDbId = idTvShow;
3475 details.m_type = "tvshow";
3476 details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3477 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3478 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3479 details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3480 details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3481 details.m_strShowPath = details.m_strPath;
3482 details.m_strShowTitle = details.m_strTitle;
3483 if (details.m_premiered.IsValid())
3484 details.m_iYear = details.m_premiered.GetYear();
3486 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3490 GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3493 CStdString strSQL = PrepareSQL("SELECT tag.strTag FROM tag, taglinks WHERE taglinks.idMedia = %i AND taglinks.media_type = 'tvshow' AND taglinks.idTag = tag.idTag ORDER BY tag.idTag", idTvShow);
3494 m_pDS2->query(strSQL.c_str());
3495 while (!m_pDS2->eof())
3497 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3501 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3502 details.m_strPictureURL.Parse();
3507 item->m_dateTime = details.m_premiered;
3508 item->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
3509 item->SetProperty("totalepisodes", details.m_iEpisode);
3510 item->SetProperty("numepisodes", details.m_iEpisode); // will be changed later to reflect watchmode setting
3511 item->SetProperty("watchedepisodes", details.m_playCount);
3512 item->SetProperty("unwatchedepisodes", details.m_iEpisode - details.m_playCount);
3514 details.m_playCount = (details.m_iEpisode <= details.m_playCount) ? 1 : 0;
3519 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3521 return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3524 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3526 CVideoInfoTag details;
3531 DWORD time = XbmcThreads::SystemClockMillis();
3532 int idEpisode = record->at(0).get_asInt();
3534 GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3535 details.m_iDbId = idEpisode;
3536 details.m_type = "episode";
3537 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3538 details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3539 CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3540 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3541 details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3542 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3543 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3544 details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3545 details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3546 details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3547 details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3548 details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3549 details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3550 details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3552 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3553 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3554 details.m_resumePoint.type = CBookmark::RESUME;
3556 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3560 GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3561 GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3563 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3564 details.m_strPictureURL.Parse();
3565 CStdString strSQL = PrepareSQL("select * from bookmark join episode on episode.c%02d=bookmark.idBookmark where episode.idEpisode=%i and bookmark.type=%i", VIDEODB_ID_EPISODE_BOOKMARK,details.m_iDbId,CBookmark::EPISODE);
3566 m_pDS2->query(strSQL.c_str());
3568 details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3571 // get streamdetails
3572 GetStreamDetails(details);
3577 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3579 return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3582 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3584 CVideoInfoTag details;
3586 unsigned int time = XbmcThreads::SystemClockMillis();
3587 int idMVideo = record->at(0).get_asInt();
3589 GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3590 details.m_iDbId = idMVideo;
3591 details.m_type = "musicvideo";
3593 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3594 details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3595 CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3596 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3597 details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3598 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3599 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3600 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3601 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3602 details.m_resumePoint.type = CBookmark::RESUME;
3604 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3609 CStdString strSQL = PrepareSQL("SELECT tag.strTag FROM tag, taglinks WHERE taglinks.idMedia = %i AND taglinks.media_type = 'musicvideo' AND taglinks.idTag = tag.idTag ORDER BY tag.idTag", idMVideo);
3610 m_pDS2->query(strSQL.c_str());
3611 while (!m_pDS2->eof())
3613 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3618 details.m_strPictureURL.Parse();
3620 // get streamdetails
3621 GetStreamDetails(details);
3626 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3630 if (!m_pDB.get()) return;
3631 if (!m_pDS2.get()) return;
3633 CStdString sql = PrepareSQL("SELECT actors.strActor,"
3634 " actorlink%s.strRole,"
3635 " actorlink%s.iOrder,"
3640 " actorlink%s.idActor=actors.idActor"
3642 " art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3643 "WHERE actorlink%s.%s=%i "
3644 "ORDER BY actorlink%s.iOrder",table.c_str(), table.c_str(), table.c_str(), table.c_str(), table.c_str(), table_id.c_str(), type_id, table.c_str());
3645 m_pDS2->query(sql.c_str());
3646 while (!m_pDS2->eof())
3649 info.strName = m_pDS2->fv(0).get_asString();
3651 for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3653 if (i->strName == info.strName)
3661 info.strRole = m_pDS2->fv(1).get_asString();
3662 info.order = m_pDS2->fv(2).get_asInt();
3663 info.thumbUrl.ParseString(m_pDS2->fv(3).get_asString());
3664 info.thumb = m_pDS2->fv(4).get_asString();
3665 cast.push_back(info);
3673 CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3677 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3678 /// \retval Returns true if the settings exist, false otherwise.
3679 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3683 // obtain the FileID (if it exists)
3684 #ifdef NEW_VIDEODB_METHODS
3685 if (NULL == m_pDB.get()) return false;
3686 if (NULL == m_pDS.get()) return false;
3687 CStdString strPath, strFileName;
3688 URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3689 CStdString strSQL=PrepareSQL("select * from settings, files, path where settings.idFile=files.idFile and path.idPath=files.idPath and path.strPath='%s' and files.strFileName='%s'", strPath.c_str() , strFileName.c_str());
3691 int idFile = GetFileId(strFilenameAndPath);
3692 if (idFile < 0) return false;
3693 if (NULL == m_pDB.get()) return false;
3694 if (NULL == m_pDS.get()) return false;
3695 // ok, now obtain the settings for this file
3696 CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3698 m_pDS->query( strSQL.c_str() );
3699 if (m_pDS->num_rows() > 0)
3700 { // get the video settings info
3701 settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3702 settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3703 settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3704 settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3705 settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3706 settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3707 settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3708 settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3709 settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3710 settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3711 settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3712 settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3713 settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3714 settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3715 settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3716 settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3717 settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3718 settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3719 settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3720 settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3721 settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3722 settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3723 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3724 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3725 settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3726 settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3727 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3728 settings.m_StereoMode = m_pDS->fv("StereoMode").get_asInt();
3729 settings.m_StereoInvert = m_pDS->fv("StereoInvert").get_asBool();
3730 settings.m_SubtitleCached = false;
3738 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3743 /// \brief Sets the settings for a particular video file
3744 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3748 if (NULL == m_pDB.get()) return ;
3749 if (NULL == m_pDS.get()) return ;
3750 int idFile = AddFile(strFilenameAndPath);
3753 CStdString strSQL = StringUtils::Format("select * from settings where idFile=%i", idFile);
3754 m_pDS->query( strSQL.c_str() );
3755 if (m_pDS->num_rows() > 0)
3759 strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3760 "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3761 "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3762 "DeinterlaceMode=%i,",
3763 setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3764 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3765 setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3766 setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3767 setting.m_DeinterlaceMode);
3769 strSQL2=PrepareSQL("ResumeTime=%i,Crop=%i,CropLeft=%i,CropRight=%i,CropTop=%i,CropBottom=%i,StereoMode=%i,StereoInvert=%i where idFile=%i\n", setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom, setting.m_StereoMode, setting.m_StereoInvert, idFile);
3771 m_pDS->exec(strSQL.c_str());
3777 strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3778 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3779 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3780 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3781 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode,StereoMode,StereoInvert) "
3783 strSQL += PrepareSQL("(%i,%i,%i,%f,%f,%f,%i,%i,%f,%i,%f,%f,%f,%f,%f,%i,%i,%i,%i,%i,%i,%i,%f,%f,%i,%i,%i,%i,%i,%i)",
3784 idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3785 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3786 setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3787 setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3788 setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3789 setting.m_DeinterlaceMode, setting.m_StereoMode, setting.m_StereoInvert);
3790 m_pDS->exec(strSQL.c_str());
3795 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3799 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3801 for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3802 SetArtForItem(mediaId, mediaType, i->first, i->second);
3805 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3809 if (NULL == m_pDB.get()) return;
3810 if (NULL == m_pDS.get()) return;
3812 // don't set <foo>.<bar> art types - these are derivative types from parent items
3813 if (artType.find('.') != string::npos)
3816 CStdString sql = PrepareSQL("SELECT art_id FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str());
3817 m_pDS->query(sql.c_str());
3820 int artId = m_pDS->fv(0).get_asInt();
3822 sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3823 m_pDS->exec(sql.c_str());
3828 sql = PrepareSQL("INSERT INTO art(media_id, media_type, type, url) VALUES (%d, '%s', '%s', '%s')", mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3829 m_pDS->exec(sql.c_str());
3834 CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3838 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3842 if (NULL == m_pDB.get()) return false;
3843 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3845 CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3846 m_pDS2->query(sql.c_str());
3847 while (!m_pDS2->eof())
3849 art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3853 return !art.empty();
3857 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3862 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3864 std::string query = PrepareSQL("SELECT url FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str());
3865 return GetSingleValue(query, m_pDS2);
3868 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::string &artType)
3870 return ExecuteQuery(PrepareSQL("DELETE FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str()));
3873 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::set<std::string> &artTypes)
3876 for (set<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
3877 result &= RemoveArtForItem(mediaId, mediaType, *i);
3882 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3886 if (NULL == m_pDB.get()) return false;
3887 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3889 // get all seasons for this show
3890 CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3891 m_pDS2->query(sql.c_str());
3893 vector< pair<int, int> > seasons;
3894 while (!m_pDS2->eof())
3896 seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3901 for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3903 map<string, string> art;
3904 GetArtForItem(i->first, "season", art);
3905 seasonArt.insert(make_pair(i->second,art));
3911 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3916 bool CVideoDatabase::GetArtTypes(const std::string &mediaType, std::vector<std::string> &artTypes)
3920 if (NULL == m_pDB.get()) return false;
3921 if (NULL == m_pDS.get()) return false;
3923 CStdString sql = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str());
3924 int numRows = RunQuery(sql);
3926 return numRows == 0;
3928 while (!m_pDS->eof())
3930 artTypes.push_back(m_pDS->fv(0).get_asString());
3938 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str());
3943 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3944 /// \retval Returns true if the stack times exist, false otherwise.
3945 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> ×)
3949 // obtain the FileID (if it exists)
3950 int idFile = GetFileId(filePath);
3951 if (idFile < 0) return false;
3952 if (NULL == m_pDB.get()) return false;
3953 if (NULL == m_pDS.get()) return false;
3954 // ok, now obtain the settings for this file
3955 CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3956 m_pDS->query( strSQL.c_str() );
3957 if (m_pDS->num_rows() > 0)
3958 { // get the video settings info
3959 CStdStringArray timeString;
3961 StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3963 for (unsigned int i = 0; i < timeString.size(); i++)
3965 times.push_back(atoi(timeString[i].c_str()));
3966 timeTotal += atoi(timeString[i].c_str());
3969 return (timeTotal > 0);
3975 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3980 /// \brief Sets the stack times for a particular video file
3981 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> ×)
3985 if (NULL == m_pDB.get()) return ;
3986 if (NULL == m_pDS.get()) return ;
3987 int idFile = AddFile(filePath);
3991 // delete any existing items
3992 m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3995 CStdString timeString = StringUtils::Format("%i", times[0]);
3996 for (unsigned int i = 1; i < times.size(); i++)
3997 timeString += StringUtils::Format(",%i", times[i]);
3999 m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
4003 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4007 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
4009 if(URIUtils::IsMultiPath(strPath))
4011 vector<CStdString> paths;
4012 CMultiPathDirectory::GetPaths(strPath, paths);
4014 for(unsigned i=0;i<paths.size();i++)
4015 RemoveContentForPath(paths[i], progress);
4020 if (NULL == m_pDB.get()) return ;
4021 if (NULL == m_pDS.get()) return ;
4025 progress->SetHeading(700);
4026 progress->SetLine(0, "");
4027 progress->SetLine(1, 313);
4028 progress->SetLine(2, 330);
4029 progress->SetPercentage(0);
4030 progress->StartModal();
4031 progress->ShowProgressBar(true);
4033 vector< pair<int,string> > paths;
4034 GetSubPaths(strPath, paths);
4036 for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
4038 bool bMvidsChecked=false;
4041 progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
4042 progress->Progress();
4045 if (HasTvShowInfo(i->second))
4046 DeleteTvShow(i->second);
4049 CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
4050 m_pDS2->query(strSQL.c_str());
4053 strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4054 m_pDS2->query(strSQL.c_str());
4055 bMvidsChecked = true;
4057 while (!m_pDS2->eof())
4059 CStdString strMoviePath;
4060 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
4061 ConstructPath(strMoviePath, i->second, strFileName);
4062 if (HasMovieInfo(strMoviePath))
4063 DeleteMovie(strMoviePath);
4064 if (HasMusicVideoInfo(strMoviePath))
4065 DeleteMusicVideo(strMoviePath);
4067 if (m_pDS2->eof() && !bMvidsChecked)
4069 strSQL =PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4070 m_pDS2->query(strSQL.c_str());
4071 bMvidsChecked = true;
4075 m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
4081 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
4087 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
4089 // if we have a multipath, set scraper for all contained paths too
4090 if(URIUtils::IsMultiPath(filePath))
4092 vector<CStdString> paths;
4093 CMultiPathDirectory::GetPaths(filePath, paths);
4095 for(unsigned i=0;i<paths.size();i++)
4096 SetScraperForPath(paths[i],scraper,settings);
4101 if (NULL == m_pDB.get()) return ;
4102 if (NULL == m_pDS.get()) return ;
4104 int idPath = AddPath(filePath);
4110 if (settings.exclude)
4111 { //NB See note in ::GetScraperForPath about strContent=='none'
4112 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
4115 { // catch clearing content, but not excluding
4116 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
4120 CStdString content = TranslateContent(scraper->Content());
4121 strSQL=PrepareSQL("update path set strContent='%s', strScraper='%s', scanRecursive=%i, useFolderNames=%i, strSettings='%s', noUpdate=%i, exclude=0 where idPath=%i", content.c_str(), scraper->ID().c_str(),settings.recurse,settings.parent_name,scraper->GetPathSettings().c_str(),settings.noupdate, idPath);
4123 m_pDS->exec(strSQL.c_str());
4127 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4131 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
4135 if (NULL == m_pDB.get()) return false;
4136 if (NULL == m_pDS.get()) return false;
4138 CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
4139 if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
4141 bool found = m_pDS->fv(0).get_asInt() > 0;
4147 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
4155 CArtItem() { art_id = 0; media_id = 0; };
4163 bool CVideoDatabase::UpdateOldVersion(int iVersion)
4167 m_pDS->exec("ALTER TABLE settings ADD VerticalShift float");
4171 // only if MySQL is used and default character set is not utf8
4172 // string data needs to be converted to proper utf8
4173 CStdString charset = m_pDS->getDatabase()->getDefaultCharset();
4174 if (!m_sqlite && !charset.empty() && charset != "utf8")
4176 map<CStdString, CStdStringArray> tables;
4177 map<CStdString, CStdStringArray>::iterator itt;
4178 CStdStringArray::iterator itc;
4180 // columns that need to be converted
4182 CStdStringArray c_columns;
4183 for (int i = 0; i < 22; i++)
4184 c_columns.push_back(StringUtils::Format("c%02d", i));
4186 tables.insert(pair<CStdString, CStdStringArray> ("episode", c_columns));
4187 tables.insert(pair<CStdString, CStdStringArray> ("movie", c_columns));
4188 tables.insert(pair<CStdString, CStdStringArray> ("musicvideo", c_columns));
4189 tables.insert(pair<CStdString, CStdStringArray> ("tvshow", c_columns));
4193 c1.push_back("strRole");
4194 tables.insert(pair<CStdString, CStdStringArray> ("actorlinkepisode", c1));
4195 tables.insert(pair<CStdString, CStdStringArray> ("actorlinkmovie", c1));
4196 tables.insert(pair<CStdString, CStdStringArray> ("actorlinktvshow", c1));
4200 c2.push_back("strActor");
4201 tables.insert(pair<CStdString, CStdStringArray> ("actors", c2));
4204 c3.push_back("strCountry");
4205 tables.insert(pair<CStdString, CStdStringArray> ("country", c3));
4208 c4.push_back("strFilename");
4209 tables.insert(pair<CStdString, CStdStringArray> ("files", c4));
4212 c5.push_back("strGenre");
4213 tables.insert(pair<CStdString, CStdStringArray> ("genre", c5));
4216 c6.push_back("strSet");
4217 tables.insert(pair<CStdString, CStdStringArray> ("sets", c6));
4220 c7.push_back("strStudio");
4221 tables.insert(pair<CStdString, CStdStringArray> ("studio", c7));
4224 c8.push_back("strPath");
4225 tables.insert(pair<CStdString, CStdStringArray> ("path", c8));
4227 for (itt = tables.begin(); itt != tables.end(); ++itt)
4230 q = PrepareSQL("UPDATE `%s` SET", itt->first.c_str());
4231 for (itc = itt->second.begin(); itc != itt->second.end(); ++itc)
4233 q += PrepareSQL(" `%s` = CONVERT(CAST(CONVERT(`%s` USING %s) AS BINARY) USING utf8)",
4234 itc->c_str(), itc->c_str(), charset.c_str());
4235 if (*itc != itt->second.back())
4246 m_pDS->exec("ALTER TABLE movie ADD c22 text");
4247 m_pDS->exec("ALTER TABLE episode ADD c22 text");
4248 m_pDS->exec("ALTER TABLE musicvideo ADD c22 text");
4249 m_pDS->exec("ALTER TABLE tvshow ADD c22 text");
4250 // Now update our tables
4251 UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH);
4252 UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH);
4253 UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH);
4254 UpdateBasePath("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, true);
4257 { // add indices for dir entry lookups
4258 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c22(255) )");
4259 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c13(255) )");
4260 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c18(255) )");
4261 m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c16(255) )");
4265 m_pDS->exec("ALTER TABLE settings ADD ScalingMethod integer");
4266 m_pDS->exec(PrepareSQL("UPDATE settings set ScalingMethod=%i", CMediaSettings::Get().GetDefaultVideoSettings().m_ScalingMethod));
4270 // Add iOrder fields to actorlink* tables to be able to list
4271 // actors by importance
4272 m_pDS->exec("ALTER TABLE actorlinkmovie ADD iOrder integer");
4273 m_pDS->exec("ALTER TABLE actorlinktvshow ADD iOrder integer");
4274 m_pDS->exec("ALTER TABLE actorlinkepisode ADD iOrder integer");
4277 { // Add basepath link to path table for faster content retrieval, and indicies
4278 m_pDS->exec("ALTER TABLE movie ADD c23 text");
4279 m_pDS->exec("ALTER TABLE episode ADD c23 text");
4280 m_pDS->exec("ALTER TABLE musicvideo ADD c23 text");
4281 m_pDS->exec("ALTER TABLE tvshow ADD c23 text");
4282 m_pDS->dropIndex("movie", "ixMovieBasePath");
4283 m_pDS->dropIndex("musicvideo", "ixMusicVideoBasePath");
4284 m_pDS->dropIndex("episode", "ixEpisodeBasePath");
4285 m_pDS->dropIndex("tvshow", "ixTVShowBasePath");
4286 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
4287 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
4288 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
4289 m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c17(12) )");
4290 // now update the base path links
4291 UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4292 UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4293 UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4294 UpdateBasePathID("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, VIDEODB_ID_TV_PARENTPATHID);
4297 { // Change INDEX for bookmark table
4298 m_pDS->dropIndex("bookmark", "ix_bookmark");
4299 m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
4303 m_pDS->exec("ALTER TABLE settings ADD DeinterlaceMode integer");
4304 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 2 WHERE Deinterlace NOT IN (0,1)"); // anything other than none: method auto => mode force
4305 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 1 WHERE Deinterlace = 1"); // method auto => mode auto
4306 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 0, Deinterlace = 1 WHERE Deinterlace = 0"); // method none => mode off, method auto
4310 { // base paths for video_ts and bdmv files was wrong (and inconsistent depending on where and when they were scanned)
4311 CStdString where = PrepareSQL(" WHERE files.strFileName LIKE 'VIDEO_TS.IFO' or files.strFileName LIKE 'index.BDMV'");
4312 UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH, false, where);
4313 UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, false, where);
4314 UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, false, where);
4315 UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4316 UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4317 UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4321 m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4322 m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4325 { // add seasons table
4326 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4327 m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
4328 // insert all seasons for each show
4329 m_pDS->query("SELECT idShow FROM tvshow");
4330 while (!m_pDS->eof())
4332 CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4337 " WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4338 m_pDS2->exec(sql.c_str());
4339 // and the "all seasons node"
4340 sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4341 m_pDS2->exec(sql.c_str());
4347 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4348 m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
4349 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; END");
4350 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; END");
4351 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; END");
4352 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; END");
4353 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; END");
4354 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; END");
4355 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); END");
4357 CMediaSettings::Get().SetVideoNeedsUpdate(63);
4358 CSettings::Get().Save();
4361 { // add idShow to episode table
4362 m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4363 m_pDS->query("SELECT idEpisode FROM episode");
4364 while (!m_pDS->eof())
4366 int idEpisode = m_pDS->fv(0).get_asInt();
4367 CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4368 m_pDS2->exec(update.c_str());
4371 m_pDS->exec("DROP TABLE tvshowlinkepisode");
4372 m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
4373 m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
4377 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4378 m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
4380 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4381 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
4382 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
4383 m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
4386 { // add idSet to movie table
4387 m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4388 m_pDS->query("SELECT idMovie FROM movie");
4389 while (!m_pDS->eof())
4391 int idMovie = m_pDS->fv(0).get_asInt();
4392 CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4393 m_pDS2->exec(sql.c_str());
4396 m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4399 { // update old art URLs
4400 m_pDS->query("select art_id,url from art where url like 'image://%%'");
4401 vector< pair<int, string> > art;
4402 while (!m_pDS->eof())
4404 art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4408 for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4409 m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4412 { // update URL encoded paths
4413 m_pDS->query("select idFile, strFilename from files");
4414 vector< pair<int, string> > files;
4415 while (!m_pDS->eof())
4417 files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4422 for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4424 std::string filename = i->second;
4425 bool update = URIUtils::UpdateUrlEncoding(filename) &&
4426 (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4430 m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4434 { // Update thumb to poster or banner as applicable
4435 CTextureDatabase db;
4438 m_pDS->query("select art_id,url,media_id,media_type from art where type='thumb' and media_type in ('movie', 'musicvideo', 'tvshow', 'season', 'set')");
4439 vector<CArtItem> art;
4440 while (!m_pDS->eof())
4442 CTextureDetails details;
4443 if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4446 item.art_id = m_pDS->fv(0).get_asInt();
4447 item.art_url = m_pDS->fv(1).get_asString();
4448 item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4449 item.media_id = m_pDS->fv(2).get_asInt();
4450 item.media_type = m_pDS->fv(3).get_asString();
4451 if (item.art_type != "thumb")
4452 art.push_back(item);
4457 for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4459 if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4460 m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4462 m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4468 m_pDS->exec("DROP TRIGGER IF EXISTS delete_movie");
4469 m_pDS->exec("DROP TRIGGER IF EXISTS delete_tvshow");
4470 m_pDS->exec("DROP TRIGGER IF EXISTS delete_musicvideo");
4471 m_pDS->exec("DROP TRIGGER IF EXISTS delete_episode");
4472 m_pDS->exec("DROP TRIGGER IF EXISTS delete_season");
4473 m_pDS->exec("DROP TRIGGER IF EXISTS delete_set");
4474 m_pDS->exec("DROP TRIGGER IF EXISTS delete_person");
4476 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
4477 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
4478 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
4480 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
4481 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
4482 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
4484 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
4485 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
4486 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
4488 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
4489 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
4491 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
4492 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
4494 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
4495 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
4497 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
4498 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
4500 m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
4501 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
4505 { // update the runtime columns
4506 vector< pair<string, int> > tables;
4507 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4508 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4509 tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4510 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4512 CStdString sql = PrepareSQL("select id%s,c%02d from %s where c%02d != ''", i->first.c_str(), i->second, (i->first=="mvideo")?"musicvideo":i->first.c_str(), i->second);
4513 m_pDS->query(sql.c_str());
4514 vector< pair<int, int> > videos;
4515 while (!m_pDS->eof())
4517 int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4519 videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4523 for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4524 m_pDS->exec(PrepareSQL("update %s set c%02d=%d where id%s=%d", (i->first=="mvideo")?"musicvideo":i->first.c_str(), i->second, j->second, i->first.c_str(), j->first));
4528 { // make indices on path, file non-unique (mysql has a prefix index, and prefixes aren't necessarily unique)
4529 m_pDS->dropIndex("path", "ix_path");
4530 m_pDS->dropIndex("files", "ix_files");
4531 m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
4532 m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
4536 m_pDS->exec("ALTER TABLE settings ADD StereoMode integer");
4537 m_pDS->exec("ALTER TABLE settings ADD StereoInvert bool");
4540 m_pDS->exec("ALTER TABLE streamdetails ADD strStereoMode text");
4542 // always recreate the view after any table change
4547 int CVideoDatabase::GetMinVersion() const
4552 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4554 SScanSettings settings;
4555 bool foundDirectly = false;
4556 ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4557 if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4558 return false; // episodes
4559 return settings.parent_name_root; // shows, movies, musicvids
4562 void CVideoDatabase::UpdateBasePath(const char *table, const char *id, int column, bool shows, const CStdString &where)
4566 query = PrepareSQL("SELECT idShow,path.strPath from tvshowlinkpath join path on tvshowlinkpath.idPath=path.idPath");
4568 query = PrepareSQL("SELECT %s.%s,path.strPath,files.strFileName from %s join files on %s.idFile=files.idFile join path on files.idPath=path.idPath", table, id, table, table);
4571 map<CStdString, bool> paths;
4572 m_pDS2->query(query.c_str());
4573 while (!m_pDS2->eof())
4575 CStdString path(m_pDS2->fv(1).get_asString());
4576 map<CStdString, bool>::iterator i = paths.find(path);
4577 if (i == paths.end())
4579 paths.insert(make_pair(path, LookupByFolders(path, shows)));
4580 i = paths.find(path);
4582 CStdString filename;
4584 ConstructPath(filename, path, m_pDS2->fv(2).get_asString());
4587 CFileItem item(filename, shows);
4588 path = item.GetBaseMoviePath(i->second);
4589 CStdString sql = PrepareSQL("UPDATE %s set c%02d='%s' where %s.%s=%i", table, column, path.c_str(), table, id, m_pDS2->fv(0).get_asInt());
4590 m_pDS->exec(sql.c_str());
4596 void CVideoDatabase::UpdateBasePathID(const char *table, const char *id, int column, int idColumn)
4598 CStdString query = PrepareSQL("SELECT %s,c%02d from %s", id, column, table);
4599 m_pDS2->query(query.c_str());
4600 while (!m_pDS2->eof())
4602 int rowID = m_pDS2->fv(0).get_asInt();
4603 CStdString path(m_pDS2->fv(1).get_asString());
4604 // find the parent path of this item
4605 int pathID = AddPath(URIUtils::GetParentPath(path));
4608 CStdString sql = PrepareSQL("UPDATE %s SET c%02d=%d WHERE %s=%d", table, idColumn, pathID, id, rowID);
4609 m_pDS->exec(sql.c_str());
4616 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4618 if(URIUtils::IsMultiPath(strPath))
4620 vector<CStdString> paths;
4621 CMultiPathDirectory::GetPaths(strPath, paths);
4624 for(unsigned i=0;i<paths.size();i++)
4625 ret |= GetPlayCounts(paths[i], items);
4630 if (URIUtils::IsPlugin(strPath))
4633 pathID = GetPathId(url.GetWithoutFilename());
4636 pathID = GetPathId(strPath);
4638 return false; // path (and thus files) aren't in the database
4643 if (NULL == m_pDB.get()) return false;
4644 if (NULL == m_pDS.get()) return false;
4646 // TODO: also test a single query for the above and below
4647 CStdString sql = PrepareSQL(
4649 " files.strFilename, files.playCount,"
4650 " bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4652 " LEFT JOIN bookmark ON"
4653 " files.idFile = bookmark.idFile AND bookmark.type = %i"
4654 " WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4656 if (RunQuery(sql) <= 0)
4659 items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4660 while (!m_pDS->eof())
4663 ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4664 CFileItemPtr item = items.Get(path);
4667 item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4668 if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4670 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4671 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4672 item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4681 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4686 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4688 int id = GetFileId(item);
4690 return 0; // not in db, so not watched
4695 if (NULL == m_pDB.get()) return -1;
4696 if (NULL == m_pDS.get()) return -1;
4698 CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4700 if (m_pDS->query(strSQL.c_str()))
4702 // there should only ever be one row returned
4703 if (m_pDS->num_rows() == 1)
4704 count = m_pDS->fv(0).get_asInt();
4711 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4716 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4718 if (NULL == m_pDB.get()) return;
4719 if (NULL == m_pDS.get()) return;
4720 if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4723 if (type == VIDEODB_CONTENT_TVSHOWS)
4724 exec = PrepareSQL("UPDATE tvshow set c%02d='%s' WHERE idShow=%i", VIDEODB_ID_TV_FANART, item.GetVideoInfoTag()->m_fanart.m_xml.c_str(), item.GetVideoInfoTag()->m_iDbId);
4725 else if (type == VIDEODB_CONTENT_MOVIES)
4726 exec = PrepareSQL("UPDATE movie set c%02d='%s' WHERE idMovie=%i", VIDEODB_ID_FANART, item.GetVideoInfoTag()->m_fanart.m_xml.c_str(), item.GetVideoInfoTag()->m_iDbId);
4730 m_pDS->exec(exec.c_str());
4732 if (type == VIDEODB_CONTENT_TVSHOWS)
4733 AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4734 else if (type == VIDEODB_CONTENT_MOVIES)
4735 AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4739 CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4743 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4746 if (item.HasProperty("original_listitem_url") &&
4747 URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4749 CFileItem item2(item);
4750 item2.SetPath(item.GetProperty("original_listitem_url").asString());
4751 id = AddFile(item2);
4758 // and mark as watched
4761 if (NULL == m_pDB.get()) return ;
4762 if (NULL == m_pDS.get()) return ;
4767 if (!date.IsValid())
4768 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4770 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4774 if (!date.IsValid())
4775 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4777 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4780 m_pDS->exec(strSQL.c_str());
4782 // We only need to announce changes to video items in the library
4783 if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4785 // Only provide the "playcount" value if it has actually changed
4786 if (item.GetVideoInfoTag()->m_playCount != count)
4789 data["playcount"] = count;
4790 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4793 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4798 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4802 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4804 SetPlayCount(item, GetPlayCount(item) + 1);
4807 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4809 SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4812 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4816 if (NULL == m_pDB.get()) return ;
4817 if (NULL == m_pDS.get()) return ;
4819 if (iType == VIDEODB_CONTENT_MOVIES)
4821 CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4824 else if (iType == VIDEODB_CONTENT_EPISODES)
4826 CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4827 content = "episode";
4829 else if (iType == VIDEODB_CONTENT_TVSHOWS)
4831 CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4834 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4836 CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4837 content = "musicvideo";
4839 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4841 CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4842 CStdString strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4843 m_pDS->exec(strSQL.c_str());
4846 if (!content.empty())
4848 SetSingleValue(iType, idMovie, FieldTitle, strNewMovieTitle);
4849 AnnounceUpdate(content, idMovie);
4854 CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4858 bool CVideoDatabase::UpdateVideoSortTitle(int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType /* = VIDEODB_CONTENT_MOVIES */)
4862 if (NULL == m_pDB.get() || NULL == m_pDS.get())
4864 if (iType != VIDEODB_CONTENT_MOVIES && iType != VIDEODB_CONTENT_TVSHOWS)
4867 CStdString content = "movie";
4868 if (iType == VIDEODB_CONTENT_TVSHOWS)
4871 if (SetSingleValue(iType, idDb, FieldSortTitle, strNewSortTitle))
4873 AnnounceUpdate(content, idDb);
4879 CLog::Log(LOGERROR, "%s (int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType) failed on ID: %i and Sort Title: %s", __FUNCTION__, idDb, strNewSortTitle.c_str());
4885 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4886 void CVideoDatabase::EraseVideoSettings()
4890 CLog::Log(LOGINFO, "Deleting settings information for all movies");
4891 m_pDS->exec("delete from settings");
4895 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4899 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4901 return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4904 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4906 return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4909 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4911 return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4914 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4918 if (NULL == m_pDB.get()) return false;
4919 if (NULL == m_pDS.get()) return false;
4922 Filter extFilter = filter;
4923 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4925 if (idContent == VIDEODB_CONTENT_MOVIES)
4927 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4928 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath, files.playCount", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4929 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on %s.id%s = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile join path on path.idPath = files.idPath",
4930 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4932 else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4934 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4935 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4936 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join episodeview on %slinktvshow.idShow = episodeview.idShow join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
4937 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4939 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4941 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4942 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath, files.playCount", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4943 extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on %s.id%s = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath",
4944 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4951 if (idContent == VIDEODB_CONTENT_MOVIES)
4953 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4954 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, count(1), count(files.playCount)", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4955 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on %s.id%s = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile",
4956 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4957 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4959 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4961 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4962 extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4963 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4964 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4966 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4968 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4969 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, count(1), count(files.playCount)", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4970 extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on %s.id%s = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile",
4971 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4972 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4980 extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4981 extFilter.group.clear();
4982 extFilter.order.clear();
4984 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4986 CVideoDbUrl videoUrl;
4987 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4990 int iRowsFound = RunQuery(strSQL);
4991 if (iRowsFound <= 0)
4992 return iRowsFound == 0;
4996 CFileItemPtr pItem(new CFileItem());
4997 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5004 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5006 map<int, pair<CStdString,int> > mapItems;
5007 map<int, pair<CStdString,int> >::iterator it;
5008 while (!m_pDS->eof())
5010 int id = m_pDS->fv(0).get_asInt();
5011 CStdString str = m_pDS->fv(1).get_asString();
5013 // was this already found?
5014 it = mapItems.find(id);
5015 if (it == mapItems.end())
5018 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5020 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5021 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
5022 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5023 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
5030 for (it = mapItems.begin(); it != mapItems.end(); ++it)
5032 CFileItemPtr pItem(new CFileItem(it->second.first));
5033 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5034 pItem->GetVideoInfoTag()->m_type = type;
5036 CVideoDbUrl itemUrl = videoUrl;
5037 CStdString path = StringUtils::Format("%ld/", it->first);
5038 itemUrl.AppendPath(path);
5039 pItem->SetPath(itemUrl.ToString());
5041 pItem->m_bIsFolder = true;
5042 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5043 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5044 if (!items.Contains(pItem->GetPath()))
5046 pItem->SetLabelPreformated(true);
5053 while (!m_pDS->eof())
5055 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5056 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5057 pItem->GetVideoInfoTag()->m_type = type;
5059 CVideoDbUrl itemUrl = videoUrl;
5060 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5061 itemUrl.AppendPath(path);
5062 pItem->SetPath(itemUrl.ToString());
5064 pItem->m_bIsFolder = true;
5065 pItem->SetLabelPreformated(true);
5066 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5067 { // fv(3) is the number of videos watched, fv(2) is the total number. We set the playcount
5068 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5069 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
5080 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5085 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5087 CStdString mediaType;
5088 if (idContent == VIDEODB_CONTENT_MOVIES)
5089 mediaType = "movie";
5090 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5091 mediaType = "tvshow";
5092 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5093 mediaType = "musicvideo";
5099 if (NULL == m_pDB.get()) return false;
5100 if (NULL == m_pDS.get()) return false;
5102 CStdString strSQL = "SELECT %s FROM taglinks ";
5104 Filter extFilter = filter;
5105 extFilter.fields = "tag.idTag, tag.strTag";
5106 extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
5108 if (idContent == (int)VIDEODB_CONTENT_MOVIES)
5109 extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
5111 extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
5112 extFilter.AppendGroup("taglinks.idTag");
5116 extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
5117 extFilter.group.clear();
5118 extFilter.order.clear();
5120 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5122 // parse the base path to get additional filters
5123 CVideoDbUrl videoUrl;
5124 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5127 int iRowsFound = RunQuery(strSQL);
5128 if (iRowsFound <= 0)
5129 return iRowsFound == 0;
5133 CFileItemPtr pItem(new CFileItem());
5134 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5141 while (!m_pDS->eof())
5143 int idTag = m_pDS->fv(0).get_asInt();
5145 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5146 pItem->m_bIsFolder = true;
5147 pItem->GetVideoInfoTag()->m_iDbId = idTag;
5148 pItem->GetVideoInfoTag()->m_type = "tag";
5150 CVideoDbUrl itemUrl = videoUrl;
5151 CStdString path = StringUtils::Format("%ld/", idTag);
5152 itemUrl.AppendPath(path);
5153 pItem->SetPath(itemUrl.ToString());
5155 if (!items.Contains(pItem->GetPath()))
5157 pItem->SetLabelPreformated(true);
5169 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5174 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool ignoreSingleMovieSets /* = false */)
5176 if (idContent != VIDEODB_CONTENT_MOVIES)
5179 return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
5182 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
5186 if (NULL == m_pDB.get()) return false;
5187 if (NULL == m_pDS.get()) return false;
5189 CVideoDbUrl videoUrl;
5190 if (!videoUrl.FromString(strBaseDir))
5193 Filter setFilter = filter;
5194 setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
5195 if (!setFilter.order.empty())
5196 setFilter.order += ",";
5197 setFilter.order += "sets.idSet";
5199 if (!GetMoviesByWhere(strBaseDir, setFilter, items))
5203 if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
5213 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5218 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5222 if (NULL == m_pDB.get()) return false;
5223 if (NULL == m_pDS.get()) return false;
5225 CStdString strSQL = "select %s from musicvideoview ";
5226 Filter extFilter = filter;
5227 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5229 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor, path.strPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
5230 extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath");
5234 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
5235 extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist");
5238 extFilter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", idArtist));
5240 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
5244 extFilter.fields = "COUNT(1)";
5245 extFilter.group.clear();
5246 extFilter.order.clear();
5248 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5250 CVideoDbUrl videoUrl;
5251 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5254 int iRowsFound = RunQuery(strSQL);
5255 if (iRowsFound <= 0)
5256 return iRowsFound == 0;
5260 CFileItemPtr pItem(new CFileItem());
5261 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5268 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5270 map<int, pair<CStdString,CStdString> > mapAlbums;
5271 map<int, pair<CStdString,CStdString> >::iterator it;
5272 while (!m_pDS->eof())
5274 int lidMVideo = m_pDS->fv(1).get_asInt();
5275 CStdString strAlbum = m_pDS->fv(0).get_asString();
5276 it = mapAlbums.find(lidMVideo);
5277 // was this genre already found?
5278 if (it == mapAlbums.end())
5281 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5282 mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5288 for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5290 if (!it->second.first.empty())
5292 CFileItemPtr pItem(new CFileItem(it->second.first));
5294 CVideoDbUrl itemUrl = videoUrl;
5295 CStdString path = StringUtils::Format("%ld/", it->first);
5296 itemUrl.AppendPath(path);
5297 pItem->SetPath(itemUrl.ToString());
5299 pItem->m_bIsFolder=true;
5300 pItem->SetLabelPreformated(true);
5301 if (!items.Contains(pItem->GetPath()))
5303 pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5311 while (!m_pDS->eof())
5313 if (!m_pDS->fv(0).get_asString().empty())
5315 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5317 CVideoDbUrl itemUrl = videoUrl;
5318 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(1).get_asInt());
5319 itemUrl.AppendPath(path);
5320 pItem->SetPath(itemUrl.ToString());
5322 pItem->m_bIsFolder=true;
5323 pItem->SetLabelPreformated(true);
5324 if (!items.Contains(pItem->GetPath()))
5326 pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5335 // CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5340 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5345 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5347 return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5350 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5352 return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5355 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5357 if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5358 { // set thumbs - ideally this should be in the normal thumb setting routines
5359 for (int i = 0; i < items.Size() && !countOnly; i++)
5361 CFileItemPtr pItem = items[i];
5362 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5363 pItem->SetIconImage("DefaultArtist.png");
5365 pItem->SetIconImage("DefaultActor.png");
5372 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5374 if (NULL == m_pDB.get()) return false;
5375 if (NULL == m_pDS.get()) return false;
5379 // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5380 // point. This means that we *MUST* filter these levels as you'll get double ups. Ideally we'd allow playcount
5381 // to filter through as we normally do for tvshows to save this happening.
5382 // Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5383 // Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5386 // General routine that the other actor/director/writer routines call
5388 // get primary genres for movies
5390 Filter extFilter = filter;
5391 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5393 if (idContent == VIDEODB_CONTENT_MOVIES)
5395 strSQL = "select %s from actors ";
5396 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5397 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on actors.idActor = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile join path on path.idPath = files.idPath",
5398 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5400 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5402 strSQL = "select %s from actors ";
5403 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5404 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on actors.idActor = %slinktvshow.id%s join episodeview on %slinktvshow.idShow = episodeview.idShow join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
5405 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5407 else if (idContent == VIDEODB_CONTENT_EPISODES)
5409 strSQL = "select %s from actors ";
5410 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5411 extFilter.AppendJoin(PrepareSQL("join %slinkepisode on actors.idActor = %slinkepisode.id%s join episodeview on %slinkepisode.idEpisode = episodeview.idEpisode join files on files.idFile = episodeview.idFile join path on path.idPath = files.idPath",
5412 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5414 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5416 strSQL = "select %s from actors ";
5417 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5418 extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on actors.idActor = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile join path on path.idPath = files.idPath",
5419 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5426 if (idContent == VIDEODB_CONTENT_MOVIES)
5428 strSQL ="select %s from actors ";
5429 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5430 extFilter.AppendJoin(PrepareSQL("join %slinkmovie on actors.idActor = %slinkmovie.id%s join movieview on %slinkmovie.idMovie = movieview.idMovie join files on files.idFile = movieview.idFile",
5431 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5432 extFilter.AppendGroup("actors.idActor");
5434 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5436 strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5437 extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5438 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5439 type.c_str(), type.c_str(), type.c_str()));
5441 else if (idContent == VIDEODB_CONTENT_EPISODES)
5443 strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5444 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5445 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5446 type.c_str(), type.c_str(), type.c_str()));
5447 extFilter.AppendGroup("actors.idActor");
5449 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5451 strSQL = "select %s from actors ";
5452 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5453 extFilter.AppendJoin(PrepareSQL("join %slinkmusicvideo on actors.idActor = %slinkmusicvideo.id%s join musicvideoview on %slinkmusicvideo.idMVideo = musicvideoview.idMVideo join files on files.idFile = musicvideoview.idFile",
5454 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5455 extFilter.AppendGroup("actors.idActor");
5463 extFilter.fields = "COUNT(1)";
5464 extFilter.group.clear();
5465 extFilter.order.clear();
5467 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5469 CVideoDbUrl videoUrl;
5470 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5474 unsigned int time = XbmcThreads::SystemClockMillis();
5475 if (!m_pDS->query(strSQL.c_str())) return false;
5476 CLog::Log(LOGDEBUG, "%s - query took %i ms",
5477 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5478 int iRowsFound = m_pDS->num_rows();
5479 if (iRowsFound == 0)
5487 CFileItemPtr pItem(new CFileItem());
5488 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5495 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5497 map<int, CActor> mapActors;
5498 map<int, CActor>::iterator it;
5500 while (!m_pDS->eof())
5502 int idActor = m_pDS->fv(0).get_asInt();
5504 actor.name = m_pDS->fv(1).get_asString();
5505 actor.thumb = m_pDS->fv(2).get_asString();
5506 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5507 actor.playcount = m_pDS->fv(3).get_asInt();
5508 it = mapActors.find(idActor);
5509 // is this actor already known?
5510 if (it == mapActors.end())
5513 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5514 mapActors.insert(pair<int, CActor>(idActor, actor));
5520 for (it=mapActors.begin();it != mapActors.end();++it)
5522 CFileItemPtr pItem(new CFileItem(it->second.name));
5524 CVideoDbUrl itemUrl = videoUrl;
5525 CStdString path = StringUtils::Format("%ld/", it->first);
5526 itemUrl.AppendPath(path);
5527 pItem->SetPath(itemUrl.ToString());
5529 pItem->m_bIsFolder=true;
5530 pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5531 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5532 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5533 pItem->GetVideoInfoTag()->m_type = type;
5539 while (!m_pDS->eof())
5543 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5545 CVideoDbUrl itemUrl = videoUrl;
5546 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5547 itemUrl.AppendPath(path);
5548 pItem->SetPath(itemUrl.ToString());
5550 pItem->m_bIsFolder=true;
5551 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5552 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5553 pItem->GetVideoInfoTag()->m_type = type;
5554 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5556 // fv(4) is the number of videos watched, fv(3) is the total number. We set the playcount
5557 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5558 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5560 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5561 pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5568 CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5569 return items.Size() > 0;
5574 CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5575 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5582 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5587 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5591 if (NULL == m_pDB.get()) return false;
5592 if (NULL == m_pDS.get()) return false;
5595 Filter extFilter = filter;
5596 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5598 if (idContent == VIDEODB_CONTENT_MOVIES)
5600 strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5601 extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5603 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5605 strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5606 extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5608 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5610 strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5611 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5619 if (idContent == VIDEODB_CONTENT_MOVIES)
5621 strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5622 extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5623 extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5625 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5626 strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5627 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5629 strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5630 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5631 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5637 CVideoDbUrl videoUrl;
5638 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5641 int iRowsFound = RunQuery(strSQL);
5642 if (iRowsFound <= 0)
5643 return iRowsFound == 0;
5645 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5647 map<int, pair<CStdString,int> > mapYears;
5648 map<int, pair<CStdString,int> >::iterator it;
5649 while (!m_pDS->eof())
5652 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5655 time.SetFromDateString(m_pDS->fv(0).get_asString());
5656 lYear = time.GetYear();
5658 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5659 lYear = m_pDS->fv(0).get_asInt();
5660 it = mapYears.find(lYear);
5661 if (it == mapYears.end())
5664 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5666 CStdString year = StringUtils::Format("%d", lYear);
5667 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5668 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5670 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5677 for (it=mapYears.begin();it != mapYears.end();++it)
5681 CFileItemPtr pItem(new CFileItem(it->second.first));
5683 CVideoDbUrl itemUrl = videoUrl;
5684 CStdString path = StringUtils::Format("%ld/", it->first);
5685 itemUrl.AppendPath(path);
5686 pItem->SetPath(itemUrl.ToString());
5688 pItem->m_bIsFolder=true;
5689 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5690 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5696 while (!m_pDS->eof())
5699 CStdString strLabel;
5700 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5703 time.SetFromDateString(m_pDS->fv(0).get_asString());
5704 lYear = time.GetYear();
5705 strLabel = StringUtils::Format("%i",lYear);
5707 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5709 lYear = m_pDS->fv(0).get_asInt();
5710 strLabel = m_pDS->fv(0).get_asString();
5717 CFileItemPtr pItem(new CFileItem(strLabel));
5719 CVideoDbUrl itemUrl = videoUrl;
5720 CStdString path = StringUtils::Format("%ld/", lYear);
5721 itemUrl.AppendPath(path);
5722 pItem->SetPath(itemUrl.ToString());
5724 pItem->m_bIsFolder=true;
5725 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5727 // fv(2) is the number of videos watched, fv(1) is the total number. We set the playcount
5728 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5729 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5732 // take care of dupes ..
5733 if (!items.Contains(pItem->GetPath()))
5745 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5750 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5754 if (NULL == m_pDB.get()) return false;
5755 if (NULL == m_pDS.get()) return false;
5757 // look for duplicate show titles and stack them into a list
5760 CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5761 CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5762 if (!m_pDS->query(strSQL.c_str())) return false;
5763 int iRows = m_pDS->num_rows();
5764 if (iRows == 0) return false; // this should never happen!
5766 { // more than one show, so stack them up
5768 while (!m_pDS->eof())
5770 strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5773 strIn[strIn.size() - 1] = ')'; // replace last , with )
5780 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5785 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5789 if (NULL == m_pDB.get()) return false;
5790 if (NULL == m_pDS.get()) return false;
5792 // parse the base path to get additional filters
5793 CVideoDbUrl videoUrl;
5794 if (!videoUrl.FromString(strBaseDir))
5797 CStdString strIn = PrepareSQL("= %i", idShow);
5798 GetStackedTvShowList(idShow, strIn);
5800 CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5802 "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5803 "seasons.idSeason, "
5804 "count(1), count(files.playCount) "
5805 "FROM episodeview ", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_PLOT, VIDEODB_ID_TV_PREMIERED, VIDEODB_ID_TV_GENRE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_MPAA);
5808 filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5809 "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5810 "JOIN files ON files.idFile = episodeview.idFile "
5811 "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5812 "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5813 filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5814 filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5816 videoUrl.AddOption("tvshowid", idShow);
5819 videoUrl.AddOption("actorid", idActor);
5820 else if (idDirector != -1)
5821 videoUrl.AddOption("directorid", idDirector);
5822 else if (idGenre != -1)
5823 videoUrl.AddOption("genreid", idGenre);
5824 else if (idYear != -1)
5825 videoUrl.AddOption("year", idYear);
5827 if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5830 int iRowsFound = RunQuery(strSQL);
5831 if (iRowsFound <= 0)
5832 return iRowsFound == 0;
5834 // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5835 CStdString showTitle = m_pDS->fv(2).get_asString();
5836 CStdString showPlot = m_pDS->fv(3).get_asString();
5837 CStdString showPremiered = m_pDS->fv(4).get_asString();
5838 CStdString showStudio = m_pDS->fv(6).get_asString();
5839 CStdString showMPAARating = m_pDS->fv(7).get_asString();
5841 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5843 map<int, CSeason> mapSeasons;
5844 map<int, CSeason>::iterator it;
5845 while (!m_pDS->eof())
5847 int iSeason = m_pDS->fv(0).get_asInt();
5848 it = mapSeasons.find(iSeason);
5850 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5855 if (it == mapSeasons.end())
5858 season.path = m_pDS->fv(1).get_asString();
5859 season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5860 season.id = m_pDS->fv(8).get_asInt();
5861 season.numEpisodes = m_pDS->fv(9).get_asInt();
5862 season.numWatched = m_pDS->fv(10).get_asInt();
5863 mapSeasons.insert(make_pair(iSeason, season));
5869 for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5871 int iSeason = it->first;
5872 CStdString strLabel;
5874 strLabel = g_localizeStrings.Get(20381);
5876 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5877 CFileItemPtr pItem(new CFileItem(strLabel));
5879 CVideoDbUrl itemUrl = videoUrl;
5880 CStdString strDir = StringUtils::Format("%ld/", it->first);
5881 itemUrl.AppendPath(strDir);
5882 pItem->SetPath(itemUrl.ToString());
5884 pItem->m_bIsFolder=true;
5885 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5886 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5887 pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5888 pItem->GetVideoInfoTag()->m_type = "season";
5889 pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5890 pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5891 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5892 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5893 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5894 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5895 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5896 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5897 pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5898 pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5899 pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5900 pItem->SetProperty("watchedepisodes", it->second.numWatched);
5901 pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5902 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5903 pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5904 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5910 while (!m_pDS->eof())
5912 int iSeason = m_pDS->fv(0).get_asInt();
5913 CStdString strLabel;
5915 strLabel = g_localizeStrings.Get(20381);
5917 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5918 CFileItemPtr pItem(new CFileItem(strLabel));
5920 CVideoDbUrl itemUrl = videoUrl;
5921 CStdString strDir = StringUtils::Format("%ld/", iSeason);
5922 itemUrl.AppendPath(strDir);
5923 pItem->SetPath(itemUrl.ToString());
5925 pItem->m_bIsFolder=true;
5926 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5927 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5928 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5929 pItem->GetVideoInfoTag()->m_type = "season";
5930 pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5931 pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5932 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5933 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5934 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5935 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5936 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5937 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5938 int totalEpisodes = m_pDS->fv(9).get_asInt();
5939 int watchedEpisodes = m_pDS->fv(10).get_asInt();
5940 pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5941 pItem->SetProperty("totalepisodes", totalEpisodes);
5942 pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5943 pItem->SetProperty("watchedepisodes", watchedEpisodes);
5944 pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5945 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5946 pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5947 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5954 // now add any linked movies
5955 if (getLinkedMovies)
5958 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5959 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5960 CFileItemList movieItems;
5961 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
5963 if (movieItems.Size() > 0)
5964 items.Append(movieItems);
5971 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5976 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5978 if (NULL == m_pDB.get() || NULL == m_pDS.get())
5981 if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5984 SortDescription sorting = sortDescription;
5985 if (sortDescription.sortBy == SortByFile ||
5986 sortDescription.sortBy == SortByTitle ||
5987 sortDescription.sortBy == SortBySortTitle ||
5988 sortDescription.sortBy == SortByLabel ||
5989 sortDescription.sortBy == SortByDateAdded ||
5990 sortDescription.sortBy == SortByRating ||
5991 sortDescription.sortBy == SortByYear ||
5992 sortDescription.sortBy == SortByLastPlayed ||
5993 sortDescription.sortBy == SortByPlaycount)
5994 sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5996 bool success = false;
5999 case MediaTypeMovie:
6000 success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
6003 case MediaTypeTvShow:
6004 success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
6007 case MediaTypeEpisode:
6008 success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
6011 case MediaTypeMusicVideo:
6012 success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
6019 items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
6023 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6025 CVideoDbUrl videoUrl;
6026 if (!videoUrl.FromString(strBaseDir))
6029 return GetItems(strBaseDir, videoUrl.GetType(), videoUrl.GetItemType(), items, filter, sortDescription);
6032 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, const CStdString &mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6034 VIDEODB_CONTENT_TYPE contentType;
6035 if (mediaType.Equals("movies"))
6036 contentType = VIDEODB_CONTENT_MOVIES;
6037 else if (mediaType.Equals("tvshows"))
6039 if (itemType.Equals("episodes"))
6040 contentType = VIDEODB_CONTENT_EPISODES;
6042 contentType = VIDEODB_CONTENT_TVSHOWS;
6044 else if (mediaType.Equals("musicvideos"))
6045 contentType = VIDEODB_CONTENT_MUSICVIDEOS;
6049 return GetItems(strBaseDir, contentType, itemType, items, filter, sortDescription);
6052 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, VIDEODB_CONTENT_TYPE mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6054 if (itemType.Equals("movies") && (mediaType == VIDEODB_CONTENT_MOVIES || mediaType == VIDEODB_CONTENT_MOVIE_SETS))
6055 return GetMoviesByWhere(strBaseDir, filter, items, sortDescription);
6056 else if (itemType.Equals("tvshows") && mediaType == VIDEODB_CONTENT_TVSHOWS)
6057 return GetTvShowsByWhere(strBaseDir, filter, items, sortDescription);
6058 else if (itemType.Equals("musicvideos") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6059 return GetMusicVideosByWhere(strBaseDir, filter, items, true, sortDescription);
6060 else if (itemType.Equals("episodes") && mediaType == VIDEODB_CONTENT_EPISODES)
6061 return GetEpisodesByWhere(strBaseDir, filter, items, true, sortDescription);
6062 else if (itemType.Equals("seasons") && mediaType == VIDEODB_CONTENT_TVSHOWS)
6063 return GetSeasonsNav(strBaseDir, items);
6064 else if (itemType.Equals("genres"))
6065 return GetGenresNav(strBaseDir, items, mediaType, filter);
6066 else if (itemType.Equals("years"))
6067 return GetYearsNav(strBaseDir, items, mediaType, filter);
6068 else if (itemType.Equals("actors"))
6069 return GetActorsNav(strBaseDir, items, mediaType, filter);
6070 else if (itemType.Equals("directors"))
6071 return GetDirectorsNav(strBaseDir, items, mediaType, filter);
6072 else if (itemType.Equals("writers"))
6073 return GetWritersNav(strBaseDir, items, mediaType, filter);
6074 else if (itemType.Equals("studios"))
6075 return GetStudiosNav(strBaseDir, items, mediaType, filter);
6076 else if (itemType.Equals("sets"))
6077 return GetSetsNav(strBaseDir, items, mediaType, filter);
6078 else if (itemType.Equals("countries"))
6079 return GetCountriesNav(strBaseDir, items, mediaType, filter);
6080 else if (itemType.Equals("tags"))
6081 return GetTagsNav(strBaseDir, items, mediaType, filter);
6082 else if (itemType.Equals("artists") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6083 return GetActorsNav(strBaseDir, items, mediaType, filter);
6084 else if (itemType.Equals("albums") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6085 return GetMusicVideoAlbumsNav(strBaseDir, items, -1, filter);
6090 CStdString CVideoDatabase::GetItemById(const CStdString &itemType, int id)
6092 if (itemType.Equals("genres"))
6093 return GetGenreById(id);
6094 else if (itemType.Equals("years"))
6095 return StringUtils::Format("%d", id);
6096 else if (itemType.Equals("actors") || itemType.Equals("directors") || itemType.Equals("artists"))
6097 return GetPersonById(id);
6098 else if (itemType.Equals("studios"))
6099 return GetStudioById(id);
6100 else if (itemType.Equals("sets"))
6101 return GetSetById(id);
6102 else if (itemType.Equals("countries"))
6103 return GetCountryById(id);
6104 else if (itemType.Equals("tags"))
6105 return GetTagById(id);
6106 else if (itemType.Equals("albums"))
6107 return GetMusicVideoAlbumById(id);
6112 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
6113 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
6114 int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
6115 const SortDescription &sortDescription /* = SortDescription() */)
6117 CVideoDbUrl videoUrl;
6118 if (!videoUrl.FromString(strBaseDir))
6122 videoUrl.AddOption("genreid", idGenre);
6123 else if (idCountry > 0)
6124 videoUrl.AddOption("countryid", idCountry);
6125 else if (idStudio > 0)
6126 videoUrl.AddOption("studioid", idStudio);
6127 else if (idDirector > 0)
6128 videoUrl.AddOption("directorid", idDirector);
6129 else if (idYear > 0)
6130 videoUrl.AddOption("year", idYear);
6131 else if (idActor > 0)
6132 videoUrl.AddOption("actorid", idActor);
6134 videoUrl.AddOption("setid", idSet);
6136 videoUrl.AddOption("tagid", idTag);
6139 return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
6142 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
6149 if (NULL == m_pDB.get()) return false;
6150 if (NULL == m_pDS.get()) return false;
6152 // parse the base path to get additional filters
6153 CVideoDbUrl videoUrl;
6154 Filter extFilter = filter;
6155 SortDescription sorting = sortDescription;
6156 if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
6161 CStdString strSQL = "select %s from movieview ";
6162 CStdString strSQLExtra;
6163 if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
6166 // Apply the limiting directly here if there's no special sorting but limiting
6167 if (extFilter.limit.empty() &&
6168 sorting.sortBy == SortByNone &&
6169 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6171 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6172 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6175 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6177 int iRowsFound = RunQuery(strSQL);
6178 if (iRowsFound <= 0)
6179 return iRowsFound == 0;
6181 // store the total value of items as a property
6182 if (total < iRowsFound)
6184 items.SetProperty("total", total);
6186 DatabaseResults results;
6187 results.reserve(iRowsFound);
6189 if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
6192 // get data from returned rows
6193 items.Reserve(results.size());
6194 const query_data &data = m_pDS->get_result_set().records;
6195 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6197 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6198 const dbiplus::sql_record* const record = data.at(targetRow);
6200 CVideoInfoTag movie = GetDetailsForMovie(record);
6201 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6202 g_passwordManager.bMasterUser ||
6203 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6205 CFileItemPtr pItem(new CFileItem(movie));
6207 CVideoDbUrl itemUrl = videoUrl;
6208 CStdString path = StringUtils::Format("%ld", movie.m_iDbId);
6209 itemUrl.AppendPath(path);
6210 pItem->SetPath(itemUrl.ToString());
6212 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
6223 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6228 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
6229 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
6230 const SortDescription &sortDescription /* = SortDescription() */)
6232 CVideoDbUrl videoUrl;
6233 if (!videoUrl.FromString(strBaseDir))
6237 videoUrl.AddOption("genreid", idGenre);
6238 else if (idStudio != -1)
6239 videoUrl.AddOption("studioid", idStudio);
6240 else if (idDirector != -1)
6241 videoUrl.AddOption("directorid", idDirector);
6242 else if (idYear != -1)
6243 videoUrl.AddOption("year", idYear);
6244 else if (idActor != -1)
6245 videoUrl.AddOption("actorid", idActor);
6246 else if (idTag != -1)
6247 videoUrl.AddOption("tagid", idTag);
6250 return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
6253 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
6259 if (NULL == m_pDB.get()) return false;
6260 if (NULL == m_pDS.get()) return false;
6264 CStdString strSQL = "SELECT %s FROM tvshowview ";
6265 CVideoDbUrl videoUrl;
6266 CStdString strSQLExtra;
6267 Filter extFilter = filter;
6268 SortDescription sorting = sortDescription;
6269 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6272 // Apply the limiting directly here if there's no special sorting but limiting
6273 if (extFilter.limit.empty() &&
6274 sorting.sortBy == SortByNone &&
6275 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6277 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6278 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6281 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6283 int iRowsFound = RunQuery(strSQL);
6284 if (iRowsFound <= 0)
6285 return iRowsFound == 0;
6287 // store the total value of items as a property
6288 if (total < iRowsFound)
6290 items.SetProperty("total", total);
6292 DatabaseResults results;
6293 results.reserve(iRowsFound);
6294 if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
6297 // get data from returned rows
6298 items.Reserve(results.size());
6299 const query_data &data = m_pDS->get_result_set().records;
6300 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6302 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6303 const dbiplus::sql_record* const record = data.at(targetRow);
6305 CFileItemPtr pItem(new CFileItem());
6306 CVideoInfoTag movie = GetDetailsForTvShow(record, false, pItem.get());
6307 if ((CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6308 g_passwordManager.bMasterUser ||
6309 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video"))) &&
6310 (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
6312 pItem->SetFromVideoInfoTag(movie);
6314 CVideoDbUrl itemUrl = videoUrl;
6315 CStdString path = StringUtils::Format("%ld/", record->at(0).get_asInt());
6316 itemUrl.AppendPath(path);
6317 pItem->SetPath(itemUrl.ToString());
6319 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6324 Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6332 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6337 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6339 if (maintainSortOrder)
6341 // save current sort order
6342 for (int i = 0; i < items.Size(); i++)
6343 items[i]->m_iprogramCount = i;
6348 case VIDEODB_CONTENT_TVSHOWS:
6351 items.Sort(SortBySortTitle, SortOrderAscending);
6354 while (i < items.Size())
6356 CFileItemPtr pItem = items.Get(i);
6357 CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6358 CStdString strFanArt = pItem->GetArt("fanart");
6361 bool bStacked = false;
6362 while (j < items.Size())
6364 CFileItemPtr jItem = items.Get(j);
6366 // matching title? append information
6367 if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6369 if (jItem->GetVideoInfoTag()->m_premiered !=
6370 pItem->GetVideoInfoTag()->m_premiered)
6377 // increment episode counts
6378 pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6379 pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6380 pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6381 pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6382 pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6384 // adjust lastplayed
6385 if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6386 pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6388 // check for fanart if not already set
6389 if (strFanArt.empty())
6390 strFanArt = jItem->GetArt("fanart");
6392 // remove duplicate entry
6395 // no match? exit loop
6399 // update playcount and fanart
6402 pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6403 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6404 if (!strFanArt.empty())
6405 pItem->SetArt("fanart", strFanArt);
6407 // increment i to j which is the next item
6412 // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6413 // so that if we eventually want to stack during scan we can utilize it.
6415 case VIDEODB_CONTENT_EPISODES:
6417 // sort by ShowTitle, Episode, Filename
6418 items.Sort(SortByEpisodeNumber, SortOrderAscending);
6421 while (i < items.Size())
6423 CFileItemPtr pItem = items.Get(i);
6424 CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6425 int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6426 int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6427 //CStdString strFanArt = pItem->GetArt("fanart");
6429 // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6430 CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6431 bool bDvdFolder = StringUtils::EndsWithNoCase(strFileNameAndPath, "video_ts.ifo");
6433 vector<CStdString> paths;
6434 paths.push_back(strFileNameAndPath);
6435 CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6438 int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6439 while (j < items.Size())
6441 CFileItemPtr jItem = items.Get(j);
6442 const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6443 CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6445 CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6446 // compare path, season, episode
6449 jTag->m_strPath.Equals(strPath) &&
6450 jTag->m_iSeason == iSeason &&
6451 jTag->m_iEpisode == iEpisode
6454 // keep checking to see if this is dvd folder
6457 bDvdFolder = StringUtils::EndsWithNoCase(jFileNameAndPath, "video_ts.ifo");
6458 // if we have a dvd folder, we stack differently
6461 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6463 paths.push_back(jFileNameAndPath);
6467 // increment playcount
6468 iPlayCount += jTag->m_playCount;
6470 // episodes dont have fanart yet
6471 //if (strFanArt.empty())
6472 // strFanArt = jItem->GetArt("fanart");
6474 paths.push_back(jFileNameAndPath);
6478 // remove duplicate entry
6482 // no match? exit loop
6486 // update playcount and fanart if we have a stacked entry
6487 if (paths.size() > 1)
6489 CStackDirectory dir;
6490 CStdString strStack;
6491 dir.ConstructStackPath(paths, strStack);
6492 pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6493 pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6494 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6496 // episodes dont have fanart yet
6497 //if (!strFanArt.empty())
6498 // pItem->SetArt("fanart", strFanArt);
6500 // increment i to j which is the next item
6506 // stack other types later
6510 if (maintainSortOrder)
6512 // restore original sort order - essential for smartplaylists
6513 items.Sort(SortByProgramCount, SortOrderAscending);
6517 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6519 CVideoDbUrl videoUrl;
6520 if (!videoUrl.FromString(strBaseDir))
6526 strIn = PrepareSQL("= %i", idShow);
6527 GetStackedTvShowList(idShow, strIn);
6529 videoUrl.AddOption("tvshowid", idShow);
6531 videoUrl.AddOption("season", idSeason);
6534 videoUrl.AddOption("genreid", idGenre);
6535 else if (idYear !=-1)
6536 videoUrl.AddOption("year", idYear);
6537 else if (idActor != -1)
6538 videoUrl.AddOption("actorid", idActor);
6540 else if (idYear != -1)
6541 videoUrl.AddOption("year", idYear);
6543 if (idDirector != -1)
6544 videoUrl.AddOption("directorid", idDirector);
6547 bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6549 if (idSeason == -1 && idShow != -1)
6550 { // add any linked movies
6552 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6553 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6554 CFileItemList movieItems;
6555 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
6557 if (movieItems.Size() > 0)
6558 items.Append(movieItems);
6564 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6571 if (NULL == m_pDB.get()) return false;
6572 if (NULL == m_pDS.get()) return false;
6576 CStdString strSQL = "select %s from episodeview ";
6577 CVideoDbUrl videoUrl;
6578 CStdString strSQLExtra;
6579 Filter extFilter = filter;
6580 SortDescription sorting = sortDescription;
6581 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6584 // Apply the limiting directly here if there's no special sorting but limiting
6585 if (extFilter.limit.empty() &&
6586 sorting.sortBy == SortByNone &&
6587 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6589 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6590 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6593 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6595 int iRowsFound = RunQuery(strSQL);
6596 if (iRowsFound <= 0)
6597 return iRowsFound == 0;
6599 // store the total value of items as a property
6600 if (total < iRowsFound)
6602 items.SetProperty("total", total);
6604 DatabaseResults results;
6605 results.reserve(iRowsFound);
6606 if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6609 // get data from returned rows
6610 items.Reserve(results.size());
6611 CLabelFormatter formatter("%H. %T", "");
6613 const query_data &data = m_pDS->get_result_set().records;
6614 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6616 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6617 const dbiplus::sql_record* const record = data.at(targetRow);
6619 CVideoInfoTag movie = GetDetailsForEpisode(record);
6620 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6621 g_passwordManager.bMasterUser ||
6622 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6624 CFileItemPtr pItem(new CFileItem(movie));
6625 formatter.FormatLabel(pItem.get());
6627 int idEpisode = record->at(0).get_asInt();
6629 CVideoDbUrl itemUrl = videoUrl;
6631 if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6632 path = StringUtils::Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6634 path = StringUtils::Format("%ld", idEpisode);
6635 itemUrl.AppendPath(path);
6636 pItem->SetPath(itemUrl.ToString());
6638 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6639 pItem->m_dateTime = movie.m_firstAired;
6640 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6651 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6656 bool CVideoDatabase::GetMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idArtist, int idDirector, int idStudio, int idAlbum, int idTag /* = -1 */, const SortDescription &sortDescription /* = SortDescription() */)
6658 CVideoDbUrl videoUrl;
6659 if (!videoUrl.FromString(strBaseDir))
6663 videoUrl.AddOption("genreid", idGenre);
6664 else if (idStudio != -1)
6665 videoUrl.AddOption("studioid", idStudio);
6666 else if (idDirector != -1)
6667 videoUrl.AddOption("directorid", idDirector);
6668 else if (idYear !=-1)
6669 videoUrl.AddOption("year", idYear);
6670 else if (idArtist != -1)
6671 videoUrl.AddOption("artistid", idArtist);
6672 else if (idTag != -1)
6673 videoUrl.AddOption("tagid", idTag);
6675 videoUrl.AddOption("albumid", idAlbum);
6678 return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6681 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6684 filter.order = "dateAdded desc, idMovie desc";
6685 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6686 return GetMoviesByWhere(strBaseDir, filter, items);
6689 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6692 filter.order = "dateAdded desc, idEpisode desc";
6693 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6694 return GetEpisodesByWhere(strBaseDir, filter, items, false);
6697 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6700 filter.order = "dateAdded desc, idMVideo desc";
6701 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6702 return GetMusicVideosByWhere(strBaseDir, filter, items);
6705 CStdString CVideoDatabase::GetGenreById(int id)
6707 return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6710 CStdString CVideoDatabase::GetCountryById(int id)
6712 return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6715 CStdString CVideoDatabase::GetSetById(int id)
6717 return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6720 CStdString CVideoDatabase::GetTagById(int id)
6722 return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6725 CStdString CVideoDatabase::GetPersonById(int id)
6727 return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6730 CStdString CVideoDatabase::GetStudioById(int id)
6732 return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6735 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6737 return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6740 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6742 return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6745 bool CVideoDatabase::HasSets() const
6749 if (NULL == m_pDB.get()) return false;
6750 if (NULL == m_pDS.get()) return false;
6752 m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6753 "JOIN sets ON sets.idSet = movieview.idSet "
6754 "GROUP BY movieview.idSet HAVING c>1");
6756 bool bResult = (m_pDS->num_rows() > 0);
6762 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6767 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6771 if (NULL == m_pDB.get()) return false;
6772 if (NULL == m_pDS2.get()) return false;
6774 // make sure we use m_pDS2, as this is called in loops using m_pDS
6775 CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6776 m_pDS2->query( strSQL.c_str() );
6780 result=m_pDS2->fv(0).get_asInt();
6787 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6792 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6795 sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6796 CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6799 return atoi(id.c_str());
6802 bool CVideoDatabase::HasContent()
6804 return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6805 HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6806 HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6809 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6811 bool result = false;
6814 if (NULL == m_pDB.get()) return false;
6815 if (NULL == m_pDS.get()) return false;
6818 if (type == VIDEODB_CONTENT_MOVIES)
6819 sql = "select count(1) from movie";
6820 else if (type == VIDEODB_CONTENT_TVSHOWS)
6821 sql = "select count(1) from tvshow";
6822 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6823 sql = "select count(1) from musicvideo";
6824 m_pDS->query( sql.c_str() );
6827 result = (m_pDS->fv(0).get_asInt() > 0);
6833 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6838 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6842 if (NULL == m_pDB.get()) return 0;
6843 if (NULL == m_pDS.get()) return 0;
6845 CStdString strSQL = StringUtils::Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6846 m_pDS->query( strSQL.c_str() );
6850 iResult = m_pDS->fv("nummovies").get_asInt();
6857 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6862 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6864 SScanSettings settings;
6865 return GetScraperForPath(strPath, settings);
6868 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6871 return GetScraperForPath(strPath, settings, dummy);
6874 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6876 foundDirectly = false;
6879 if (strPath.empty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6882 CStdString strPath2;
6884 if (URIUtils::IsMultiPath(strPath))
6885 strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6889 CStdString strPath1 = URIUtils::GetDirectory(strPath2);
6890 int idPath = GetPathId(strPath1);
6894 CStdString strSQL=PrepareSQL("select path.strContent,path.strScraper,path.scanRecursive,path.useFolderNames,path.strSettings,path.noUpdate,path.exclude from path where path.idPath=%i",idPath);
6895 m_pDS->query( strSQL.c_str() );
6899 CONTENT_TYPE content = CONTENT_NONE;
6901 { // path is stored in db
6903 if (m_pDS->fv("path.exclude").get_asBool())
6905 settings.exclude = true;
6907 return ScraperPtr();
6909 settings.exclude = false;
6911 // try and ascertain scraper for this path
6912 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6913 StringUtils::ToLower(strcontent);
6914 content = TranslateContent(strcontent);
6916 //FIXME paths stored should not have empty strContent
6917 //assert(content != CONTENT_NONE);
6918 CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6921 if (!scraperID.empty() &&
6922 CAddonMgr::Get().GetAddon(scraperID, addon))
6924 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6926 return ScraperPtr();
6928 // store this path's content & settings
6929 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6930 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6931 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6932 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6936 if (content == CONTENT_NONE)
6937 { // this path is not saved in db
6938 // we must drill up until a scraper is configured
6939 CStdString strParent;
6940 while (URIUtils::GetParentPath(strPath1, strParent))
6944 CStdString strSQL=PrepareSQL("select path.strContent,path.strScraper,path.scanRecursive,path.useFolderNames,path.strSettings,path.noUpdate, path.exclude from path where strPath='%s'",strParent.c_str());
6945 m_pDS->query(strSQL.c_str());
6947 CONTENT_TYPE content = CONTENT_NONE;
6951 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6952 StringUtils::ToLower(strcontent);
6953 if (m_pDS->fv("path.exclude").get_asBool())
6955 settings.exclude = true;
6961 content = TranslateContent(strcontent);
6964 if (content != CONTENT_NONE &&
6965 CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6967 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6968 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6969 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6970 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6971 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6972 settings.exclude = false;
6976 strPath1 = strParent;
6981 if (!scraper || scraper->Content() == CONTENT_NONE)
6982 return ScraperPtr();
6984 if (scraper->Content() == CONTENT_TVSHOWS)
6986 settings.recurse = 0;
6987 if(settings.parent_name) // single show
6989 settings.parent_name_root = settings.parent_name = (iFound == 1);
6993 settings.parent_name_root = settings.parent_name = (iFound == 2);
6996 else if (scraper->Content() == CONTENT_MOVIES)
6998 settings.recurse = settings.recurse - (iFound-1);
6999 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
7001 else if (scraper->Content() == CONTENT_MUSICVIDEOS)
7003 settings.recurse = settings.recurse - (iFound-1);
7004 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
7009 return ScraperPtr();
7011 foundDirectly = (iFound == 1);
7016 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7018 return ScraperPtr();
7021 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
7023 SScanSettings settings;
7024 bool foundDirectly = false;
7025 ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
7028 if (scraper->Content() == CONTENT_TVSHOWS)
7029 { // check for episodes or seasons. Assumptions are:
7030 // 1. if episodes are in the path then we're in episodes.
7031 // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
7032 // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
7033 CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
7034 m_pDS->query( sql.c_str() );
7035 if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
7037 return foundDirectly ? "tvshows" : "seasons";
7039 return TranslateContent(scraper->Content());
7044 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
7050 if (NULL == m_pDB.get()) return;
7051 if (NULL == m_pDS.get()) return;
7053 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7054 strSQL=PrepareSQL("select genre.idGenre,genre.strGenre,path.strPath from genre,genrelinkmovie,movie,path,files where genre.idGenre=genrelinkmovie.idGenre and genrelinkmovie.idMovie=movie.idMovie and files.idFile=movie.idFile and path.idPath=files.idPath and genre.strGenre like '%%%s%%'",strSearch.c_str());
7056 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
7057 m_pDS->query( strSQL.c_str() );
7059 while (!m_pDS->eof())
7061 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7062 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
7063 *CMediaSourceSettings::Get().GetSources("video")))
7069 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7070 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7071 pItem->SetPath("videodb://movies/genres/"+ strDir);
7072 pItem->m_bIsFolder=true;
7080 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7084 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
7090 if (NULL == m_pDB.get()) return;
7091 if (NULL == m_pDS.get()) return;
7093 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7094 strSQL=PrepareSQL("select country.idCountry,country.strCountry,path.strPath from country,countrylinkmovie,movie,path,files where country.idCountry=countrylinkmovie.idCountry and countrylinkmovie.idMovie=movie.idMovie and files.idFile=movie.idFile and path.idPath=files.idPath and country.strCountry like '%%%s%%'",strSearch.c_str());
7096 strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
7097 m_pDS->query( strSQL.c_str() );
7099 while (!m_pDS->eof())
7101 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7102 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
7103 *CMediaSourceSettings::Get().GetSources("video")))
7109 CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
7110 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
7111 pItem->SetPath("videodb://movies/genres/"+ strDir);
7112 pItem->m_bIsFolder=true;
7120 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7124 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
7130 if (NULL == m_pDB.get()) return;
7131 if (NULL == m_pDS.get()) return;
7133 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7134 strSQL=PrepareSQL("select genre.idGenre,genre.strGenre,path.strPath from genre,genrelinktvshow,tvshow,path,tvshowlinkpath where genre.idGenre=genrelinktvshow.idGenre and genrelinktvshow.idShow=tvshow.idShow and path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=tvshow.idShow and genre.strGenre like '%%%s%%'",strSearch.c_str());
7136 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
7137 m_pDS->query( strSQL.c_str() );
7139 while (!m_pDS->eof())
7141 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7142 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7148 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7149 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7150 pItem->SetPath("videodb://tvshows/genres/"+ strDir);
7151 pItem->m_bIsFolder=true;
7159 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7163 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
7169 if (NULL == m_pDB.get()) return;
7170 if (NULL == m_pDS.get()) return;
7172 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7173 strSQL=PrepareSQL("select actors.idActor,actors.strActor,path.strPath from actorlinkmovie,actors,movie,files,path where actors.idActor=actorlinkmovie.idActor and actorlinkmovie.idMovie=movie.idMovie and files.idFile=movie.idFile and files.idPath=path.idPath and actors.strActor like '%%%s%%'",strSearch.c_str());
7175 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from actorlinkmovie,actors,movie where actors.idActor=actorlinkmovie.idActor and actorlinkmovie.idMovie=movie.idMovie and actors.strActor like '%%%s%%'",strSearch.c_str());
7176 m_pDS->query( strSQL.c_str() );
7178 while (!m_pDS->eof())
7180 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7181 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7187 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7188 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7189 pItem->SetPath("videodb://movies/actors/"+ strDir);
7190 pItem->m_bIsFolder=true;
7198 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7202 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
7208 if (NULL == m_pDB.get()) return;
7209 if (NULL == m_pDS.get()) return;
7211 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7212 strSQL=PrepareSQL("select actors.idActor,actors.strActor,path.strPath from actorlinktvshow,actors,tvshow,path,tvshowlinkpath where actors.idActor=actorlinktvshow.idActor and actorlinktvshow.idShow=tvshow.idShow and tvshowlinkpath.idPath=tvshow.idShow and tvshowlinkpath.idPath=path.idPath and actors.strActor like '%%%s%%'",strSearch.c_str());
7214 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from actorlinktvshow,actors,tvshow where actors.idActor=actorlinktvshow.idActor and actorlinktvshow.idShow=tvshow.idShow and actors.strActor like '%%%s%%'",strSearch.c_str());
7215 m_pDS->query( strSQL.c_str() );
7217 while (!m_pDS->eof())
7219 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7220 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7226 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7227 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7228 pItem->SetPath("videodb://tvshows/actors/"+ strDir);
7229 pItem->m_bIsFolder=true;
7237 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7241 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
7247 if (NULL == m_pDB.get()) return;
7248 if (NULL == m_pDS.get()) return;
7251 if (!strSearch.empty())
7252 strLike = "and actors.strActor like '%%%s%%'";
7253 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7254 strSQL=PrepareSQL("select actors.idActor,actors.strActor,path.strPath from artistlinkmusicvideo,actors,musicvideo,files,path where actors.idActor=artistlinkmusicvideo.idArtist and artistlinkmusicvideo.idMVideo=musicvideo.idMVideo and files.idFile=musicvideo.idFile and files.idPath=path.idPath "+strLike,strSearch.c_str());
7256 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
7257 m_pDS->query( strSQL.c_str() );
7259 while (!m_pDS->eof())
7261 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7262 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7268 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7269 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7270 pItem->SetPath("videodb://musicvideos/artists/"+ strDir);
7271 pItem->m_bIsFolder=true;
7279 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7283 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
7289 if (NULL == m_pDB.get()) return;
7290 if (NULL == m_pDS.get()) return;
7292 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7293 strSQL=PrepareSQL("select genre.idGenre,genre.strGenre,path.strPath from genre,genrelinkmusicvideo,musicvideo,path,files where genre.idGenre=genrelinkmusicvideo.idGenre and genrelinkmusicvideo.idMVideo = musicvideo.idMVideo and files.idFile=musicvideo.idFile and path.idPath=files.idPath and genre.strGenre like '%%%s%%'",strSearch.c_str());
7295 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
7296 m_pDS->query( strSQL.c_str() );
7298 while (!m_pDS->eof())
7300 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7301 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7307 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7308 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7309 pItem->SetPath("videodb://musicvideos/genres/"+ strDir);
7310 pItem->m_bIsFolder=true;
7318 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7322 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7328 if (NULL == m_pDB.get()) return;
7329 if (NULL == m_pDS.get()) return;
7331 strSQL = StringUtils::Format("SELECT DISTINCT"
7332 " musicvideo.c%02d,"
7333 " musicvideo.idMVideo,"
7338 " files.idFile=musicvideo.idFile"
7340 " path.idPath=files.idPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
7341 if (!strSearch.empty())
7342 strSQL += PrepareSQL(" WHERE musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM, strSearch.c_str());
7344 m_pDS->query( strSQL.c_str() );
7346 while (!m_pDS->eof())
7348 if (m_pDS->fv(0).get_asString().empty())
7354 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7355 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7361 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7362 CStdString strDir = StringUtils::Format("%ld", m_pDS->fv(1).get_asInt());
7363 pItem->SetPath("videodb://musicvideos/titles/"+ strDir);
7364 pItem->m_bIsFolder=false;
7372 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7376 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7382 if (NULL == m_pDB.get()) return;
7383 if (NULL == m_pDS.get()) return;
7385 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7386 strSQL = PrepareSQL("select musicvideo.idMVideo,musicvideo.c%02d,musicvideo.c%02d,path.strPath from musicvideo,files,path where files.idFile=musicvideo.idFile and files.idPath=path.idPath and musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM,VIDEODB_ID_MUSICVIDEO_TITLE,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7388 strSQL = PrepareSQL("select musicvideo.idMVideo,musicvideo.c%02d,musicvideo.c%02d from musicvideo where musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_ALBUM,VIDEODB_ID_MUSICVIDEO_TITLE,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7389 m_pDS->query( strSQL.c_str() );
7391 while (!m_pDS->eof())
7393 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7394 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7400 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7401 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7403 pItem->SetPath("videodb://"+ strDir);
7404 pItem->m_bIsFolder=false;
7412 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7416 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7423 if (NULL == m_pDB.get()) return false;
7424 if (NULL == m_pDS.get()) return false;
7428 CStdString strSQL = "select %s from musicvideoview ";
7429 CVideoDbUrl videoUrl;
7430 CStdString strSQLExtra;
7431 Filter extFilter = filter;
7432 SortDescription sorting = sortDescription;
7433 if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7436 // Apply the limiting directly here if there's no special sorting but limiting
7437 if (extFilter.limit.empty() &&
7438 sorting.sortBy == SortByNone &&
7439 (sorting.limitStart > 0 || sorting.limitEnd > 0))
7441 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7442 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7445 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7447 int iRowsFound = RunQuery(strSQL);
7448 if (iRowsFound <= 0)
7449 return iRowsFound == 0;
7451 // store the total value of items as a property
7452 if (total < iRowsFound)
7454 items.SetProperty("total", total);
7456 DatabaseResults results;
7457 results.reserve(iRowsFound);
7458 if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7461 // get data from returned rows
7462 items.Reserve(results.size());
7463 // get songs from returned subtable
7464 const query_data &data = m_pDS->get_result_set().records;
7465 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7467 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7468 const dbiplus::sql_record* const record = data.at(targetRow);
7470 CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7471 if (!checkLocks || CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7472 g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
7474 CFileItemPtr item(new CFileItem(musicvideo));
7476 CVideoDbUrl itemUrl = videoUrl;
7477 CStdString path = StringUtils::Format("%ld", record->at(0).get_asInt());
7478 itemUrl.AppendPath(path);
7479 item->SetPath(itemUrl.ToString());
7481 item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7492 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7497 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7501 if (NULL == m_pDB.get()) return 0;
7502 if (NULL == m_pDS.get()) return 0;
7504 CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7505 if (!m_pDS->query(strSQL.c_str())) return 0;
7507 if (m_pDS->num_rows() == 0)
7512 songIDs.reserve(m_pDS->num_rows());
7513 while (!m_pDS->eof())
7515 songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7519 return songIDs.size();
7523 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7528 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7534 if (NULL == m_pDB.get()) return false;
7535 if (NULL == m_pDS.get()) return false;
7537 // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7538 CStdString strSQL = StringUtils::Format("select * from musicvideoview where %s", strWhere.c_str());
7539 strSQL += PrepareSQL(" order by RANDOM() limit 1");
7540 CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7542 if (!m_pDS->query(strSQL.c_str()))
7544 int iRowsFound = m_pDS->num_rows();
7545 if (iRowsFound != 1)
7550 *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7551 CStdString path = StringUtils::Format("videodb://musicvideos/titles/%ld",item->GetVideoInfoTag()->m_iDbId);
7552 item->SetPath(path);
7553 idSong = m_pDS->fv("idMVideo").get_asInt();
7554 item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7560 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7565 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7569 if (NULL == m_pDB.get()) return -1;
7570 if (NULL == m_pDS.get()) return -1;
7573 if (strAlbum.empty() && strTitle.empty())
7574 { // we want to return matching artists only
7575 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7576 strSQL=PrepareSQL("select distinct actors.idActor,path.strPath from artistlinkmusicvideo,actors,musicvideo,files,path where actors.idActor=artistlinkmusicvideo.idArtist and artistlinkmusicvideo.idMVideo=musicvideo.idMVideo and files.idFile=musicvideo.idFile and files.idPath=path.idPath and actors.strActor like '%s'",strArtist.c_str());
7578 strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7581 { // we want to return the matching musicvideo
7582 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7583 strSQL = PrepareSQL("select musicvideo.idMVideo from musicvideo,files,path,artistlinkmusicvideo,actors where files.idFile=musicvideo.idFile and files.idPath=path.idPath and musicvideo.%c02d like '%s' and musicvideo.%c02d like '%s' and artistlinkmusicvideo.idMVideo=musicvideo.idMVideo and artistlinkmusicvideo.idArtist=actors.idActors and actors.strActor like '%s'",VIDEODB_ID_MUSICVIDEO_ALBUM,strAlbum.c_str(),VIDEODB_ID_MUSICVIDEO_TITLE,strTitle.c_str(),strArtist.c_str());
7585 strSQL = PrepareSQL("select musicvideo.idMVideo from musicvideo join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideo.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist where musicvideo.c%02d like '%s' and musicvideo.c%02d like '%s' and actors.strActor like '%s'",VIDEODB_ID_MUSICVIDEO_ALBUM,strAlbum.c_str(),VIDEODB_ID_MUSICVIDEO_TITLE,strTitle.c_str(),strArtist.c_str());
7587 m_pDS->query( strSQL.c_str() );
7592 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7593 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7599 int lResult = m_pDS->fv(0).get_asInt();
7605 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7610 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7616 if (NULL == m_pDB.get()) return;
7617 if (NULL == m_pDS.get()) return;
7619 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7620 strSQL = PrepareSQL("select movie.idMovie,movie.c%02d,path.strPath, movie.idSet from movie,files,path where files.idFile=movie.idFile and files.idPath=path.idPath and movie.c%02d like '%%%s%%'",VIDEODB_ID_TITLE,VIDEODB_ID_TITLE,strSearch.c_str());
7622 strSQL = PrepareSQL("select movie.idMovie,movie.c%02d, movie.idSet from movie where movie.c%02d like '%%%s%%'",VIDEODB_ID_TITLE,VIDEODB_ID_TITLE,strSearch.c_str());
7623 m_pDS->query( strSQL.c_str() );
7625 while (!m_pDS->eof())
7627 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7628 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7634 int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7635 int setId = m_pDS->fv("movie.idSet").get_asInt();
7636 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7638 if (setId <= 0 || !CSettings::Get().GetBool("videolibrary.groupmoviesets"))
7639 path = StringUtils::Format("videodb://movies/titles/%i", movieId);
7641 path = StringUtils::Format("videodb://movies/sets/%i/%i", setId, movieId);
7642 pItem->SetPath(path);
7643 pItem->m_bIsFolder=false;
7651 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7655 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7661 if (NULL == m_pDB.get()) return;
7662 if (NULL == m_pDS.get()) return;
7664 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7665 strSQL = PrepareSQL("select tvshow.idShow,tvshow.c%02d,path.strPath from tvshow,path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and tvshowlinkpath.idShow=tvshow.idShow and tvshow.c%02d like '%%%s%%'",VIDEODB_ID_TV_TITLE,VIDEODB_ID_TV_TITLE,strSearch.c_str());
7667 strSQL = PrepareSQL("select tvshow.idShow,tvshow.c%02d from tvshow where tvshow.c%02d like '%%%s%%'",VIDEODB_ID_TV_TITLE,VIDEODB_ID_TV_TITLE,strSearch.c_str());
7668 m_pDS->query( strSQL.c_str() );
7670 while (!m_pDS->eof())
7672 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7673 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7679 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7680 CStdString strDir = StringUtils::Format("tvshows/titles/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7682 pItem->SetPath("videodb://"+ strDir);
7683 pItem->m_bIsFolder=true;
7684 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7692 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7696 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7702 if (NULL == m_pDB.get()) return;
7703 if (NULL == m_pDS.get()) return;
7705 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7706 strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,episode.idShow,tvshow.c%02d,path.strPath from episode,files,path,tvshow where files.idFile=episode.idFile and episode.idShow=tvshow.idShow and files.idPath=path.idPath and episode.c%02d like '%%%s%%'",VIDEODB_ID_EPISODE_TITLE,VIDEODB_ID_EPISODE_SEASON,VIDEODB_ID_TV_TITLE,VIDEODB_ID_EPISODE_TITLE,strSearch.c_str());
7708 strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,episode.idShow,tvshow.c%02d from episode,tvshow where tvshow.idShow=episode.idShow and episode.c%02d like '%%%s%%'",VIDEODB_ID_EPISODE_TITLE,VIDEODB_ID_EPISODE_SEASON,VIDEODB_ID_TV_TITLE,VIDEODB_ID_EPISODE_TITLE,strSearch.c_str());
7709 m_pDS->query( strSQL.c_str() );
7711 while (!m_pDS->eof())
7713 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7714 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7720 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7721 CStdString path = StringUtils::Format("videodb://tvshows/titles/%ld/%ld/%ld",m_pDS->fv("episode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
7722 pItem->SetPath(path);
7723 pItem->m_bIsFolder=false;
7731 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7735 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7737 // Alternative searching - not quite as fast though due to
7738 // retrieving all information
7739 // Filter filter(PrepareSQL("c%02d like '%s%%' or c%02d like '%% %s%%'", VIDEODB_ID_MUSICVIDEO_TITLE, strSearch.c_str(), VIDEODB_ID_MUSICVIDEO_TITLE, strSearch.c_str()));
7740 // GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
7745 if (NULL == m_pDB.get()) return;
7746 if (NULL == m_pDS.get()) return;
7748 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7749 strSQL = PrepareSQL("select musicvideo.idMVideo,musicvideo.c%02d,path.strPath from musicvideo,files,path where files.idFile=musicvideo.idFile and files.idPath=path.idPath and musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_TITLE,VIDEODB_ID_MUSICVIDEO_TITLE,strSearch.c_str());
7751 strSQL = PrepareSQL("select musicvideo.idMVideo,musicvideo.c%02d from musicvideo where musicvideo.c%02d like '%%%s%%'",VIDEODB_ID_MUSICVIDEO_TITLE,VIDEODB_ID_MUSICVIDEO_TITLE,strSearch.c_str());
7752 m_pDS->query( strSQL.c_str() );
7754 while (!m_pDS->eof())
7756 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7757 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7763 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7764 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7766 pItem->SetPath("videodb://"+ strDir);
7767 pItem->m_bIsFolder=false;
7775 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7779 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7781 // Alternative searching - not quite as fast though due to
7782 // retrieving all information
7784 // filter.where = PrepareSQL("c%02d like '%s%%' or c%02d like '%% %s%%'", VIDEODB_ID_EPISODE_PLOT, strSearch.c_str(), VIDEODB_ID_EPISODE_PLOT, strSearch.c_str());
7785 // filter.where += PrepareSQL("or c%02d like '%s%%' or c%02d like '%% %s%%'", VIDEODB_ID_EPISODE_TITLE, strSearch.c_str(), VIDEODB_ID_EPISODE_TITLE, strSearch.c_str());
7786 // GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
7792 if (NULL == m_pDB.get()) return;
7793 if (NULL == m_pDS.get()) return;
7795 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7796 strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,episode.idShow,tvshow.c%02d,path.strPath from episode,files,path,tvshow where files.idFile=episode.idFile and files.idPath=path.idPath and tvshow.idShow=episode.idShow and episode.c%02d like '%%%s%%'",VIDEODB_ID_EPISODE_TITLE,VIDEODB_ID_EPISODE_SEASON,VIDEODB_ID_TV_TITLE,VIDEODB_ID_EPISODE_PLOT,strSearch.c_str());
7798 strSQL = PrepareSQL("select episode.idEpisode,episode.c%02d,episode.c%02d,episode.idShow,tvshow.c%02d from episode,tvshow where tvshow.idShow=episode.idShow and episode.c%02d like '%%%s%%'",VIDEODB_ID_EPISODE_TITLE,VIDEODB_ID_EPISODE_SEASON,VIDEODB_ID_TV_TITLE,VIDEODB_ID_EPISODE_PLOT,strSearch.c_str());
7799 m_pDS->query( strSQL.c_str() );
7801 while (!m_pDS->eof())
7803 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7804 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7810 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7811 CStdString path = StringUtils::Format("videodb://tvshows/titles/%ld/%ld/%ld",m_pDS->fv("episode.idShow").get_asInt(),m_pDS->fv(2).get_asInt(),m_pDS->fv(0).get_asInt());
7812 pItem->SetPath(path);
7813 pItem->m_bIsFolder=false;
7821 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7825 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7831 if (NULL == m_pDB.get()) return;
7832 if (NULL == m_pDS.get()) return;
7834 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7835 strSQL = PrepareSQL("select movie.idMovie, movie.c%02d, path.strPath from movie,files,path where files.idFile=movie.idFile and files.idPath=path.idPath and (movie.c%02d like '%%%s%%' or movie.c%02d like '%%%s%%' or movie.c%02d like '%%%s%%')",VIDEODB_ID_TITLE,VIDEODB_ID_PLOT,strSearch.c_str(),VIDEODB_ID_PLOTOUTLINE,strSearch.c_str(),VIDEODB_ID_TAGLINE,strSearch.c_str());
7837 strSQL = PrepareSQL("select movie.idMovie, movie.c%02d from movie where (movie.c%02d like '%%%s%%' or movie.c%02d like '%%%s%%' or movie.c%02d like '%%%s%%')",VIDEODB_ID_TITLE,VIDEODB_ID_PLOT,strSearch.c_str(),VIDEODB_ID_PLOTOUTLINE,strSearch.c_str(),VIDEODB_ID_TAGLINE,strSearch.c_str());
7839 m_pDS->query( strSQL.c_str() );
7841 while (!m_pDS->eof())
7843 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7844 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7850 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7851 CStdString path = StringUtils::Format("videodb://movies/titles/%ld", m_pDS->fv(0).get_asInt());
7852 pItem->SetPath(path);
7853 pItem->m_bIsFolder=false;
7863 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7867 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7873 if (NULL == m_pDB.get()) return;
7874 if (NULL == m_pDS.get()) return;
7876 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7877 strSQL = PrepareSQL("select distinct directorlinkmovie.idDirector,actors.strActor,path.strPath from movie,files,path,actors,directorlinkmovie where files.idFile=movie.idFile and files.idPath=path.idPath and directorlinkmovie.idMovie=movie.idMovie and directorlinkmovie.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7879 strSQL = PrepareSQL("select distinct directorlinkmovie.idDirector,actors.strActor from movie,actors,directorlinkmovie where directorlinkmovie.idMovie=movie.idMovie and directorlinkmovie.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7881 m_pDS->query( strSQL.c_str() );
7883 while (!m_pDS->eof())
7885 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7886 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7892 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7893 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7895 pItem->SetPath("videodb://movies/directors/"+ strDir);
7896 pItem->m_bIsFolder=true;
7904 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7908 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7914 if (NULL == m_pDB.get()) return;
7915 if (NULL == m_pDS.get()) return;
7917 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7918 strSQL = PrepareSQL("select distinct directorlinktvshow.idDirector,actors.strActor,path.strPath from tvshow,path,actors,directorlinktvshow,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and tvshowlinkpath.idShow=tvshow.idShow and directorlinktvshow.idShow=tvshow.idShow and directorlinktvshow.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7920 strSQL = PrepareSQL("select distinct directorlinktvshow.idDirector,actors.strActor from tvshow,actors,directorlinktvshow where directorlinktvshow.idShow=tvshow.idShow and directorlinktvshow.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7922 m_pDS->query( strSQL.c_str() );
7924 while (!m_pDS->eof())
7926 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7927 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7933 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7934 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7936 pItem->SetPath("videodb://tvshows/studios/"+ strDir);
7937 pItem->m_bIsFolder=true;
7945 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7949 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7955 if (NULL == m_pDB.get()) return;
7956 if (NULL == m_pDS.get()) return;
7958 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7959 strSQL = PrepareSQL("select distinct directorlinkmusicvideo.idDirector,actors.strActor,path.strPath from musicvideo,files,path,actors,directorlinkmusicvideo where files.idFile=musicvideo.idFile and files.idPath=path.idPath and directorlinkmusicvideo.idMVideo=musicvideo.idMVideo and directorlinkmusicvideo.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7961 strSQL = PrepareSQL("select distinct directorlinkmusicvideo.idDirector,actors.strActor from musicvideo,actors,directorlinkmusicvideo where directorlinkmusicvideo.idMVideo=musicvideo.idMVideo and directorlinkmusicvideo.idDirector=actors.idActor and actors.strActor like '%%%s%%'",strSearch.c_str());
7963 m_pDS->query( strSQL.c_str() );
7965 while (!m_pDS->eof())
7967 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7968 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7974 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7975 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7977 pItem->SetPath("videodb://musicvideos/albums/"+ strDir);
7978 pItem->m_bIsFolder=true;
7986 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7990 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7992 CGUIDialogProgress *progress=NULL;
7995 if (NULL == m_pDB.get()) return;
7996 if (NULL == m_pDS.get()) return;
7998 unsigned int time = XbmcThreads::SystemClockMillis();
7999 CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
8000 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
8004 // find all the files
8008 if (paths->size() == 0)
8010 RollbackTransaction();
8011 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8015 CStdString strPaths;
8016 for (std::set<int>::const_iterator i = paths->begin(); i != paths->end(); ++i)
8017 strPaths += StringUtils::Format(",%i",*i);
8018 sql = PrepareSQL("select * from files, path where files.idPath=path.idPath and path.idPath in (%s)", strPaths.substr(1).c_str());
8021 sql = "select * from files, path where files.idPath = path.idPath";
8023 m_pDS->query(sql.c_str());
8024 if (m_pDS->num_rows() == 0) return;
8028 handle->SetTitle(g_localizeStrings.Get(700));
8029 handle->SetText("");
8031 else if (showProgress)
8033 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8036 progress->SetHeading(700);
8037 progress->SetLine(0, "");
8038 progress->SetLine(1, 313);
8039 progress->SetLine(2, 330);
8040 progress->SetPercentage(0);
8041 progress->StartModal();
8042 progress->ShowProgressBar(true);
8046 CStdString filesToDelete = "";
8047 CStdString moviesToDelete = "";
8048 CStdString episodesToDelete = "";
8049 CStdString musicVideosToDelete = "";
8051 std::vector<int> movieIDs;
8052 std::vector<int> episodeIDs;
8053 std::vector<int> musicVideoIDs;
8055 int total = m_pDS->num_rows();
8058 while (!m_pDS->eof())
8060 CStdString path = m_pDS->fv("path.strPath").get_asString();
8061 CStdString fileName = m_pDS->fv("files.strFileName").get_asString();
8062 CStdString fullPath;
8063 ConstructPath(fullPath,path,fileName);
8065 // get the first stacked file
8066 if (URIUtils::IsStack(fullPath))
8067 fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
8069 // remove optical, non-existing files
8070 if (URIUtils::IsOnDVD(fullPath) || !CFile::Exists(fullPath, false))
8071 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
8077 progress->SetPercentage(current * 100 / total);
8078 progress->Progress();
8079 if (progress->IsCanceled())
8083 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8089 handle->SetPercentage(current/(float)total*100);
8096 // Add any files that don't have a valid idPath entry to the filesToDelete list.
8097 sql = "select files.idFile from files where idPath not in (select idPath from path)";
8098 m_pDS->query(sql.c_str());
8099 while (!m_pDS->eof())
8101 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
8106 if ( ! filesToDelete.empty() )
8108 StringUtils::TrimRight(filesToDelete, ",");
8109 // now grab them movies
8110 sql = PrepareSQL("select idMovie from movie where idFile in (%s)",filesToDelete.c_str());
8111 m_pDS->query(sql.c_str());
8112 while (!m_pDS->eof())
8114 movieIDs.push_back(m_pDS->fv(0).get_asInt());
8115 moviesToDelete += m_pDS->fv(0).get_asString() + ",";
8119 // now grab them episodes
8120 sql = PrepareSQL("select idEpisode from episode where idFile in (%s)",filesToDelete.c_str());
8121 m_pDS->query(sql.c_str());
8122 while (!m_pDS->eof())
8124 episodeIDs.push_back(m_pDS->fv(0).get_asInt());
8125 episodesToDelete += m_pDS->fv(0).get_asString() + ",";
8130 // now grab them musicvideos
8131 sql = PrepareSQL("select idMVideo from musicvideo where idFile in (%s)",filesToDelete.c_str());
8132 m_pDS->query(sql.c_str());
8133 while (!m_pDS->eof())
8135 musicVideoIDs.push_back(m_pDS->fv(0).get_asInt());
8136 musicVideosToDelete += m_pDS->fv(0).get_asString() + ",";
8144 progress->SetPercentage(100);
8145 progress->Progress();
8148 if ( ! filesToDelete.empty() )
8150 filesToDelete = "(" + filesToDelete + ")";
8151 CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
8152 sql = "delete from files where idFile in " + filesToDelete;
8153 m_pDS->exec(sql.c_str());
8155 CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
8156 sql = "delete from streamdetails where idFile in " + filesToDelete;
8157 m_pDS->exec(sql.c_str());
8159 CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
8160 sql = "delete from bookmark where idFile in " + filesToDelete;
8161 m_pDS->exec(sql.c_str());
8163 CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
8164 sql = "delete from settings where idFile in " + filesToDelete;
8165 m_pDS->exec(sql.c_str());
8167 CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
8168 sql = "delete from stacktimes where idFile in " + filesToDelete;
8169 m_pDS->exec(sql.c_str());
8172 if ( ! moviesToDelete.empty() )
8174 moviesToDelete = "(" + StringUtils::TrimRight(moviesToDelete, ",") + ")";
8176 CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
8177 sql = "delete from movie where idMovie in " + moviesToDelete;
8178 m_pDS->exec(sql.c_str());
8180 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
8181 sql = "delete from actorlinkmovie where idMovie in " + moviesToDelete;
8182 m_pDS->exec(sql.c_str());
8184 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
8185 sql = "delete from directorlinkmovie where idMovie in " + moviesToDelete;
8186 m_pDS->exec(sql.c_str());
8188 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
8189 sql = "delete from writerlinkmovie where idMovie in " + moviesToDelete;
8190 m_pDS->exec(sql.c_str());
8192 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
8193 sql = "delete from genrelinkmovie where idMovie in " + moviesToDelete;
8194 m_pDS->exec(sql.c_str());
8196 CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
8197 sql = "delete from countrylinkmovie where idMovie in " + moviesToDelete;
8198 m_pDS->exec(sql.c_str());
8200 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
8201 sql = "delete from studiolinkmovie where idMovie in " + moviesToDelete;
8202 m_pDS->exec(sql.c_str());
8205 if ( ! episodesToDelete.empty() )
8207 episodesToDelete = "(" + StringUtils::TrimRight(episodesToDelete, ",") + ")";
8209 CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
8210 sql = "delete from episode where idEpisode in " + episodesToDelete;
8211 m_pDS->exec(sql.c_str());
8213 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
8214 sql = "delete from actorlinkepisode where idEpisode in " + episodesToDelete;
8215 m_pDS->exec(sql.c_str());
8217 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
8218 sql = "delete from directorlinkepisode where idEpisode in " + episodesToDelete;
8219 m_pDS->exec(sql.c_str());
8221 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
8222 sql = "delete from writerlinkepisode where idEpisode in " + episodesToDelete;
8223 m_pDS->exec(sql.c_str());
8226 CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and have content set...", __FUNCTION__);
8227 sql = "select * from path where not (strContent='' and strSettings='' and strHash='' and exclude!=1)";
8228 m_pDS->query(sql.c_str());
8230 while (!m_pDS->eof())
8232 if (!CDirectory::Exists(m_pDS->fv("path.strPath").get_asString()))
8233 strIds += StringUtils::Format("%i,", m_pDS->fv("path.idPath").get_asInt());
8237 if (!strIds.empty())
8239 StringUtils::TrimRight(strIds, ",");
8240 sql = PrepareSQL("delete from path where idPath in (%s)",strIds.c_str());
8241 m_pDS->exec(sql.c_str());
8242 sql = PrepareSQL("delete from tvshowlinkpath where idPath in (%s)",strIds.c_str());
8243 m_pDS->exec(sql.c_str());
8245 sql = "delete from tvshowlinkpath where idPath not in (select idPath from path)";
8246 m_pDS->exec(sql.c_str());
8248 CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
8249 sql = "delete from tvshow where idShow not in (select idShow from tvshowlinkpath)";
8250 m_pDS->exec(sql.c_str());
8252 std::vector<int> tvshowIDs;
8253 CStdString showsToDelete;
8254 sql = "select tvshow.idShow from tvshow "
8255 "join tvshowlinkpath on tvshow.idShow=tvshowlinkpath.idShow "
8256 "join path on path.idPath=tvshowlinkpath.idPath "
8257 "where tvshow.idShow not in (select idShow from episode) "
8258 "and path.strContent=''";
8259 m_pDS->query(sql.c_str());
8260 while (!m_pDS->eof())
8262 tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
8263 showsToDelete += m_pDS->fv(0).get_asString() + ",";
8267 if (!showsToDelete.empty())
8269 sql = "delete from tvshow where idShow in (" + StringUtils::TrimRight(showsToDelete, ",") + ")";
8270 m_pDS->exec(sql.c_str());
8273 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
8274 sql = "delete from actorlinktvshow where idShow not in (select idShow from tvshow)";
8275 m_pDS->exec(sql.c_str());
8277 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
8278 sql = "delete from directorlinktvshow where idShow not in (select idShow from tvshow)";
8279 m_pDS->exec(sql.c_str());
8281 CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
8282 sql = "delete from tvshowlinkpath where idShow not in (select idShow from tvshow)";
8283 m_pDS->exec(sql.c_str());
8285 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
8286 sql = "delete from genrelinktvshow where idShow not in (select idShow from tvshow)";
8287 m_pDS->exec(sql.c_str());
8289 CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
8290 sql = "delete from seasons where idShow not in (select idShow from tvshow)";
8291 m_pDS->exec(sql.c_str());
8293 CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
8294 sql = "delete from movielinktvshow where idShow not in (select idShow from tvshow)";
8295 m_pDS->exec(sql.c_str());
8296 sql = "delete from movielinktvshow where idMovie not in (select distinct idMovie from movie)";
8297 m_pDS->exec(sql.c_str());
8299 if ( ! musicVideosToDelete.empty() )
8301 musicVideosToDelete = "(" + StringUtils::TrimRight(musicVideosToDelete, ",") + ")";
8303 CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8304 sql = "delete from musicvideo where idMVideo in " + musicVideosToDelete;
8305 m_pDS->exec(sql.c_str());
8307 CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8308 sql = "delete from artistlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8309 m_pDS->exec(sql.c_str());
8311 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8312 sql = "delete from directorlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8313 m_pDS->exec(sql.c_str());
8315 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8316 sql = "delete from genrelinkmusicvideo where idMVideo in " + musicVideosToDelete;
8317 m_pDS->exec(sql.c_str());
8319 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8320 sql = "delete from studiolinkmusicvideo where idMVideo in " + musicVideosToDelete;
8321 m_pDS->exec(sql.c_str());
8324 CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8325 sql = StringUtils::Format("delete from path where strContent='' and strSettings='' and strHash='' and exclude!=1 "
8326 "and idPath not in (select distinct idPath from files) "
8327 "and idPath not in (select distinct idPath from tvshowlinkpath) "
8328 "and idPath not in (select distinct c%02d from movie) "
8329 "and idPath not in (select distinct c%02d from tvshow) "
8330 "and idPath not in (select distinct c%02d from episode) "
8331 "and idPath not in (select distinct c%02d from musicvideo)"
8332 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8333 m_pDS->exec(sql.c_str());
8335 CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8336 sql = "delete from genre where idGenre not in (select distinct idGenre from genrelinkmovie) and idGenre not in (select distinct idGenre from genrelinktvshow) and idGenre not in (select distinct idGenre from genrelinkmusicvideo)";
8337 m_pDS->exec(sql.c_str());
8339 CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8340 sql = "delete from country where idCountry not in (select distinct idCountry from countrylinkmovie)";
8341 m_pDS->exec(sql.c_str());
8343 CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8344 sql = "delete from actors where idActor not in (select distinct idActor from actorlinkmovie) and idActor not in (select distinct idDirector from directorlinkmovie) and idActor not in (select distinct idWriter from writerlinkmovie) and idActor not in (select distinct idActor from actorlinktvshow) and idActor not in (select distinct idActor from actorlinkepisode) and idActor not in (select distinct idDirector from directorlinktvshow) and idActor not in (select distinct idDirector from directorlinkepisode) and idActor not in (select distinct idWriter from writerlinkepisode) and idActor not in (select distinct idArtist from artistlinkmusicvideo) and idActor not in (select distinct idDirector from directorlinkmusicvideo)";
8345 m_pDS->exec(sql.c_str());
8347 CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8348 sql = "delete from studio where idStudio not in (select distinct idStudio from studiolinkmovie) and idStudio not in (select distinct idStudio from studiolinkmusicvideo) and idStudio not in (select distinct idStudio from studiolinktvshow)";
8349 m_pDS->exec(sql.c_str());
8351 CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8352 sql = "delete from sets where idSet not in (select distinct idSet from movie)";
8353 m_pDS->exec(sql.c_str());
8355 CommitTransaction();
8358 handle->SetTitle(g_localizeStrings.Get(331));
8362 CUtil::DeleteVideoDatabaseDirectoryCache();
8364 time = XbmcThreads::SystemClockMillis() - time;
8365 CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8367 for (unsigned int i = 0; i < movieIDs.size(); i++)
8368 AnnounceRemove("movie", movieIDs[i]);
8370 for (unsigned int i = 0; i < episodeIDs.size(); i++)
8371 AnnounceRemove("episode", episodeIDs[i]);
8373 for (unsigned int i = 0; i < tvshowIDs.size(); i++)
8374 AnnounceRemove("tvshow", tvshowIDs[i]);
8376 for (unsigned int i = 0; i < musicVideoIDs.size(); i++)
8377 AnnounceRemove("musicvideo", musicVideoIDs[i]);
8381 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8382 RollbackTransaction();
8387 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8390 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8393 CFileItemList items;
8394 GetTvShowsByWhere("videodb://tvshows/titles/", "", items);
8395 CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8396 CDirectory::Create(showPath);
8397 for (int i = 0; i < items.Size(); i++)
8399 // create a folder in this directory
8400 CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8401 CStdString TVFolder = URIUtils::AddFileToFolder(showPath, showName);
8402 if (CDirectory::Create(TVFolder))
8403 { // right - grab the episodes and dump them as well
8404 CFileItemList episodes;
8405 Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8406 GetEpisodesByWhere("videodb://tvshows/titles/", filter, episodes);
8407 for (int i = 0; i < episodes.Size(); i++)
8409 CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8410 CStdString episode = StringUtils::Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8412 CStdString episodePath = URIUtils::AddFileToFolder(TVFolder, episode);
8414 if (file.OpenForWrite(episodePath))
8421 GetMoviesByWhere("videodb://movies/titles/", "", items);
8422 CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8423 CDirectory::Create(moviePath);
8424 for (int i = 0; i < items.Size(); i++)
8426 CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8427 CStdString movie = StringUtils::Format("%s.avi", tag->m_strTitle.c_str());
8429 if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8434 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8436 CGUIDialogProgress *progress=NULL;
8439 if (NULL == m_pDB.get()) return;
8440 if (NULL == m_pDS.get()) return;
8441 if (NULL == m_pDS2.get()) return;
8443 // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8444 auto_ptr<Dataset> pDS;
8445 pDS.reset(m_pDB->CreateDataset());
8446 if (NULL == pDS.get()) return;
8448 auto_ptr<Dataset> pDS2;
8449 pDS2.reset(m_pDB->CreateDataset());
8450 if (NULL == pDS2.get()) return;
8452 // if we're exporting to a single folder, we export thumbs as well
8453 CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8454 CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8455 CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8456 CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8457 CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8458 CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8464 CDirectory::Remove(exportRoot);
8465 CDirectory::Create(exportRoot);
8466 CDirectory::Create(actorsDir);
8467 CDirectory::Create(moviesDir);
8468 CDirectory::Create(musicvideosDir);
8469 CDirectory::Create(tvshowsDir);
8472 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8474 CStdString sql = "select * from movieview";
8476 m_pDS->query(sql.c_str());
8480 progress->SetHeading(647);
8481 progress->SetLine(0, 650);
8482 progress->SetLine(1, "");
8483 progress->SetLine(2, "");
8484 progress->SetPercentage(0);
8485 progress->StartModal();
8486 progress->ShowProgressBar(true);
8489 int total = m_pDS->num_rows();
8492 // create our xml document
8493 CXBMCTinyXML xmlDoc;
8494 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8495 xmlDoc.InsertEndChild(decl);
8496 TiXmlNode *pMain = NULL;
8501 TiXmlElement xmlMainElement("videodb");
8502 pMain = xmlDoc.InsertEndChild(xmlMainElement);
8503 XMLUtils::SetInt(pMain,"version", GetExportVersion());
8506 while (!m_pDS->eof())
8508 CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8509 // strip paths to make them relative
8510 if (StringUtils::StartsWith(movie.m_strTrailer, movie.m_strPath))
8511 movie.m_strTrailer = movie.m_strTrailer.substr(movie.m_strPath.size());
8512 map<string, string> artwork;
8513 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8515 TiXmlElement additionalNode("art");
8516 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8517 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8518 movie.Save(pMain, "movie", true, &additionalNode);
8521 movie.Save(pMain, "movie", !singleFiles);
8523 // reset old skip state
8528 progress->SetLine(1, movie.m_strTitle);
8529 progress->SetPercentage(current * 100 / total);
8530 progress->Progress();
8531 if (progress->IsCanceled())
8539 CFileItem item(movie.m_strFileNameAndPath,false);
8540 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8542 if (!item.Exists(false))
8544 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8549 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8551 if (item.IsOpticalMediaFile())
8553 nfoFile = URIUtils::AddFileToFolder(
8554 URIUtils::GetParentPath(nfoFile),
8555 URIUtils::GetFileName(nfoFile));
8558 if (overwrite || !CFile::Exists(nfoFile, false))
8560 if(!xmlDoc.SaveFile(nfoFile))
8562 CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8563 bSkip = ExportSkipEntry(nfoFile);
8580 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8581 xmlDoc.InsertEndChild(decl);
8584 if (images && !bSkip)
8588 CStdString strFileName(movie.m_strTitle);
8589 if (movie.m_iYear > 0)
8590 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8591 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8593 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8595 CStdString savedThumb = item.GetLocalArt(i->first, false);
8596 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8599 ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8606 // find all musicvideos
8607 sql = "select * from musicvideoview";
8609 m_pDS->query(sql.c_str());
8611 total = m_pDS->num_rows();
8614 while (!m_pDS->eof())
8616 CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8617 map<string, string> artwork;
8618 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8620 TiXmlElement additionalNode("art");
8621 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8622 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8623 movie.Save(pMain, "musicvideo", true, &additionalNode);
8626 movie.Save(pMain, "musicvideo", !singleFiles);
8628 // reset old skip state
8633 progress->SetLine(1, movie.m_strTitle);
8634 progress->SetPercentage(current * 100 / total);
8635 progress->Progress();
8636 if (progress->IsCanceled())
8644 CFileItem item(movie.m_strFileNameAndPath,false);
8645 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8647 if (!item.Exists(false))
8649 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8654 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8656 if (overwrite || !CFile::Exists(nfoFile, false))
8658 if(!xmlDoc.SaveFile(nfoFile))
8660 CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8661 bSkip = ExportSkipEntry(nfoFile);
8678 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8679 xmlDoc.InsertEndChild(decl);
8681 if (images && !bSkip)
8685 CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8686 if (movie.m_iYear > 0)
8687 strFileName += StringUtils::Format("_%i", movie.m_iYear);
8688 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8690 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8692 CStdString savedThumb = item.GetLocalArt(i->first, false);
8693 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8701 // repeat for all tvshows
8702 sql = "SELECT * FROM tvshowview";
8703 m_pDS->query(sql.c_str());
8705 total = m_pDS->num_rows();
8708 while (!m_pDS->eof())
8710 CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8712 map<int, map<string, string> > seasonArt;
8713 GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8715 map<string, string> artwork;
8716 if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8718 TiXmlElement additionalNode("art");
8719 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8720 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8721 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8723 TiXmlElement seasonNode("season");
8724 seasonNode.SetAttribute("num", i->first);
8725 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8726 XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8727 additionalNode.InsertEndChild(seasonNode);
8729 tvshow.Save(pMain, "tvshow", true, &additionalNode);
8732 tvshow.Save(pMain, "tvshow", !singleFiles);
8734 // reset old skip state
8739 progress->SetLine(1, tvshow.m_strTitle);
8740 progress->SetPercentage(current * 100 / total);
8741 progress->Progress();
8742 if (progress->IsCanceled())
8750 // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8751 if (URIUtils::IsMultiPath(tvshow.m_strPath))
8752 tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8754 CFileItem item(tvshow.m_strPath, true);
8755 if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8757 if (!item.Exists(false))
8759 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8764 CStdString nfoFile = URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo");
8766 if (overwrite || !CFile::Exists(nfoFile, false))
8768 if(!xmlDoc.SaveFile(nfoFile))
8770 CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8771 bSkip = ExportSkipEntry(nfoFile);
8788 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8789 xmlDoc.InsertEndChild(decl);
8791 if (images && !bSkip)
8794 item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8796 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8798 CStdString savedThumb = item.GetLocalArt(i->first, true);
8799 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8803 ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8805 // export season thumbs
8806 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8810 seasonThumb = "season-all";
8811 else if (i->first == 0)
8812 seasonThumb = "season-specials";
8814 seasonThumb = StringUtils::Format("season%02i", i->first);
8815 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8817 CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8818 if (!i->second.empty())
8819 CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8824 // now save the episodes from this show
8825 sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8826 pDS->query(sql.c_str());
8827 CStdString showDir(item.GetPath());
8831 CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8832 map<string, string> artwork;
8833 if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8835 TiXmlElement additionalNode("art");
8836 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8837 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8838 episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8840 else if (!singleFiles)
8841 episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8843 episode.Save(pMain, "episodedetails", !singleFiles);
8845 // multi-episode files need dumping to the same XML
8846 while (singleFiles && !pDS->eof() &&
8847 episode.m_iFileId == pDS->fv("idFile").get_asInt())
8849 episode = GetDetailsForEpisode(pDS, true);
8850 episode.Save(pMain, "episodedetails", !singleFiles);
8854 // reset old skip state
8857 CFileItem item(episode.m_strFileNameAndPath, false);
8858 if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8860 if (!item.Exists(false))
8862 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8867 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8869 if (overwrite || !CFile::Exists(nfoFile, false))
8871 if(!xmlDoc.SaveFile(nfoFile))
8873 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8874 bSkip = ExportSkipEntry(nfoFile);
8891 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8892 xmlDoc.InsertEndChild(decl);
8895 if (images && !bSkip)
8899 CStdString epName = StringUtils::Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8900 item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8902 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8904 CStdString savedThumb = item.GetLocalArt(i->first, false);
8905 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8908 ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8917 if (singleFiles && progress)
8919 progress->SetPercentage(100);
8920 progress->Progress();
8925 // now dump path info
8926 set<CStdString> paths;
8928 TiXmlElement xmlPathElement("paths");
8929 TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8930 for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8932 bool foundDirectly = false;
8933 SScanSettings settings;
8934 ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8935 if (info && foundDirectly)
8937 TiXmlElement xmlPathElement2("path");
8938 TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8939 XMLUtils::SetString(pPath,"url", *iter);
8940 XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8941 XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8942 XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8943 XMLUtils::SetString(pPath,"scraperpath", info->ID());
8946 xmlDoc.SaveFile(xmlFile);
8951 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8958 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8960 CStdString strPath(strDir);
8963 strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8964 if (!CDirectory::Exists(strPath))
8966 CDirectory::Create(strPath);
8967 CFile::SetHidden(strPath, true);
8971 for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8974 item.SetLabel(iter->strName);
8975 if (!iter->thumb.empty())
8977 CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8978 CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8983 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8985 CStdString strParent;
8986 URIUtils::GetParentPath(nfoFile,strParent);
8987 CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8989 bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8992 CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8994 CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8999 void CVideoDatabase::ImportFromXML(const CStdString &path)
9001 CGUIDialogProgress *progress=NULL;
9004 if (NULL == m_pDB.get()) return;
9005 if (NULL == m_pDS.get()) return;
9007 CXBMCTinyXML xmlDoc;
9008 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
9011 TiXmlElement *root = xmlDoc.RootElement();
9014 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
9017 progress->SetHeading(648);
9018 progress->SetLine(0, 649);
9019 progress->SetLine(1, 330);
9020 progress->SetLine(2, "");
9021 progress->SetPercentage(0);
9022 progress->StartModal();
9023 progress->ShowProgressBar(true);
9027 XMLUtils::GetInt(root, "version", iVersion);
9029 CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
9032 TiXmlElement *movie = root->FirstChildElement();
9035 // first count the number of items...
9038 if (strnicmp(movie->Value(), "movie", 5)==0 ||
9039 strnicmp(movie->Value(), "tvshow", 6)==0 ||
9040 strnicmp(movie->Value(), "musicvideo",10)==0 )
9042 movie = movie->NextSiblingElement();
9045 CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
9046 CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
9047 CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
9048 CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
9049 CVideoInfoScanner scanner;
9050 // add paths first (so we have scraper settings available)
9051 TiXmlElement *path = root->FirstChildElement("paths");
9052 path = path->FirstChildElement();
9056 if (XMLUtils::GetString(path,"url",strPath) && !strPath.empty())
9060 if (XMLUtils::GetString(path,"content", content) && !content.empty())
9061 { // check the scraper exists, if so store the path
9064 XMLUtils::GetString(path,"scraperpath",id);
9065 if (CAddonMgr::Get().GetAddon(id, addon))
9067 SScanSettings settings;
9068 ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
9069 // FIXME: scraper settings are not exported?
9070 scraper->SetPathSettings(TranslateContent(content), "");
9071 XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
9072 XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
9073 SetScraperForPath(strPath,scraper,settings);
9076 path = path->NextSiblingElement();
9078 movie = root->FirstChildElement();
9082 if (strnicmp(movie->Value(), "movie", 5) == 0)
9085 CFileItem item(info);
9086 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
9087 CStdString filename = info.m_strTitle;
9088 if (info.m_iYear > 0)
9089 filename += StringUtils::Format("_%i", info.m_iYear);
9090 CFileItem artItem(item);
9091 artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
9092 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9093 item.SetArt(artItem.GetArt());
9094 scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
9097 else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
9100 CFileItem item(info);
9101 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
9102 CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
9103 if (info.m_iYear > 0)
9104 filename += StringUtils::Format("_%i", info.m_iYear);
9105 CFileItem artItem(item);
9106 artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
9107 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9108 item.SetArt(artItem.GetArt());
9109 scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
9112 else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
9114 // load the TV show in. NOTE: This deletes all episodes under the TV Show, which may not be
9115 // what we desire. It may make better sense to only delete (or even better, update) the show information
9117 URIUtils::AddSlashAtEnd(info.m_strPath);
9118 DeleteTvShow(info.m_strPath);
9119 CFileItem showItem(info);
9120 bool useFolders = info.m_basePath.empty() ? LookupByFolders(showItem.GetPath(), true) : false;
9121 CFileItem artItem(showItem);
9122 CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
9123 artItem.SetPath(artPath);
9124 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9125 showItem.SetArt(artItem.GetArt());
9126 int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
9128 map<int, map<string, string> > seasonArt;
9129 artItem.GetVideoInfoTag()->m_strPath = artPath;
9130 scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
9131 for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
9133 int seasonID = AddSeason(showID, i->first);
9134 SetArtForItem(seasonID, "season", i->second);
9137 // now load the episodes
9138 TiXmlElement *episode = movie->FirstChildElement("episodedetails");
9141 // no need to delete the episode info, due to the above deletion
9144 CFileItem item(info);
9145 CStdString filename = StringUtils::Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
9146 CFileItem artItem(item);
9147 artItem.SetPath(GetSafeFile(artPath, filename));
9148 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9149 item.SetArt(artItem.GetArt());
9150 scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
9151 episode = episode->NextSiblingElement("episodedetails");
9154 movie = movie->NextSiblingElement();
9155 if (progress && total)
9157 progress->SetPercentage(current * 100 / total);
9158 progress->SetLine(2, info.m_strTitle);
9159 progress->Progress();
9160 if (progress->IsCanceled())
9163 RollbackTransaction();
9169 CommitTransaction();
9173 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
9174 RollbackTransaction();
9180 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
9182 if (!node) return false;
9183 const TiXmlNode *art = node->FirstChild();
9184 while (art && art->FirstChild())
9186 artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
9187 art = art->NextSibling();
9189 return !artwork.empty();
9192 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
9194 if (URIUtils::IsStack(strFileName) ||
9195 URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
9196 strDest = strFileName;
9198 strDest = URIUtils::AddFileToFolder(strPath, strFileName);
9201 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
9203 if (URIUtils::IsStack(strFileNameAndPath) || StringUtils::StartsWithNoCase(strFileNameAndPath, "rar://") || StringUtils::StartsWithNoCase(strFileNameAndPath, "zip://"))
9205 URIUtils::GetParentPath(strFileNameAndPath,strPath);
9206 strFileName = strFileNameAndPath;
9208 else if (URIUtils::IsPlugin(strFileNameAndPath))
9210 CURL url(strFileNameAndPath);
9211 strPath = url.GetWithoutFilename();
9212 strFileName = strFileNameAndPath;
9215 URIUtils::Split(strFileNameAndPath,strPath, strFileName);
9218 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
9220 SScanSettings settings;
9222 ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
9223 SetPathHash(strPath,"");
9226 if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
9228 if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
9230 CStdString strParent;
9231 URIUtils::GetParentPath(strPath,strParent);
9232 SetPathHash(strParent,"");
9237 bool CVideoDatabase::CommitTransaction()
9239 if (CDatabase::CommitTransaction())
9240 { // number of items in the db has likely changed, so recalculate
9241 g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
9242 g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
9243 g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
9249 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, int dbField, const std::string &strValue)
9254 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9257 string strTable, strField;
9258 if (type == VIDEODB_CONTENT_MOVIES)
9261 strField = "idMovie";
9263 else if (type == VIDEODB_CONTENT_TVSHOWS)
9265 strTable = "tvshow";
9266 strField = "idShow";
9268 else if (type == VIDEODB_CONTENT_EPISODES)
9270 strTable = "episode";
9271 strField = "idEpisode";
9273 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
9275 strTable = "musicvideo";
9276 strField = "idMVideo";
9279 if (strTable.empty())
9282 return SetSingleValue(strTable, StringUtils::Format("c%02u", dbField), strValue, strField, dbId);
9286 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
9291 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, Field dbField, const std::string &strValue)
9293 MediaType mediaType = DatabaseUtils::MediaTypeFromVideoContentType(type);
9294 if (mediaType == MediaTypeNone)
9297 int dbFieldIndex = DatabaseUtils::GetField(dbField, mediaType);
9298 if (dbFieldIndex < 0)
9301 return SetSingleValue(type, dbId, dbFieldIndex, strValue);
9304 bool CVideoDatabase::SetSingleValue(const std::string &table, const std::string &fieldName, const std::string &strValue,
9305 const std::string &conditionName /* = "" */, int conditionValue /* = -1 */)
9307 if (table.empty() || fieldName.empty())
9313 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9316 sql = PrepareSQL("UPDATE %s SET %s='%s'", table.c_str(), fieldName.c_str(), strValue.c_str());
9317 if (!conditionName.empty())
9318 sql += PrepareSQL(" WHERE %s=%u", conditionName.c_str(), conditionValue);
9319 if (m_pDS->exec(sql.c_str()) == 0)
9324 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, sql.c_str());
9329 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
9331 CStdString safeThumb(name);
9332 StringUtils::Replace(safeThumb, ' ', '_');
9333 return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
9336 void CVideoDatabase::AnnounceRemove(std::string content, int id)
9339 data["type"] = content;
9341 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9344 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9347 data["type"] = content;
9349 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9352 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9354 CStdString path(strPath);
9356 if(URIUtils::IsMultiPath(path))
9358 vector<CStdString> paths;
9359 CMultiPathDirectory::GetPaths(path, paths);
9361 for(unsigned i=0;i<paths.size();i++)
9362 GetItemsForPath(content, paths[i], items);
9364 return items.Size() > 0;
9367 int pathID = GetPathId(path);
9371 if (content == "movies")
9373 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9374 GetMoviesByWhere("videodb://movies/titles/", filter, items);
9376 else if (content == "episodes")
9378 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9379 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
9381 else if (content == "tvshows")
9383 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9384 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
9386 else if (content == "musicvideos")
9388 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9389 GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
9391 for (int i = 0; i < items.Size(); i++)
9392 items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9393 return items.Size() > 0;
9396 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9398 if (!videoUrl.IsValid())
9401 std::string type = videoUrl.GetType();
9402 std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9403 const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9404 CUrlOptions::UrlOptions::const_iterator option;
9406 if (type == "movies")
9408 option = options.find("genreid");
9409 if (option != options.end())
9411 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9412 filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9415 option = options.find("genre");
9416 if (option != options.end())
9418 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9419 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9422 option = options.find("countryid");
9423 if (option != options.end())
9425 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9426 filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9429 option = options.find("country");
9430 if (option != options.end())
9432 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9433 filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9436 option = options.find("studioid");
9437 if (option != options.end())
9439 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9440 filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9443 option = options.find("studio");
9444 if (option != options.end())
9446 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9447 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9450 option = options.find("directorid");
9451 if (option != options.end())
9453 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9454 filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9457 option = options.find("director");
9458 if (option != options.end())
9460 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9461 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9464 option = options.find("year");
9465 if (option != options.end())
9466 filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9468 option = options.find("actorid");
9469 if (option != options.end())
9471 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9472 filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9475 option = options.find("actor");
9476 if (option != options.end())
9478 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9479 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9482 option = options.find("setid");
9483 if (option != options.end())
9484 filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9486 option = options.find("set");
9487 if (option != options.end())
9489 filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9490 filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9493 option = options.find("tagid");
9494 if (option != options.end())
9496 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9497 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9500 option = options.find("tag");
9501 if (option != options.end())
9503 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9504 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9507 else if (type == "tvshows")
9509 if (itemType == "tvshows")
9511 option = options.find("genreid");
9512 if (option != options.end())
9514 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9515 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9518 option = options.find("genre");
9519 if (option != options.end())
9521 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9522 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9525 option = options.find("studioid");
9526 if (option != options.end())
9528 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9529 filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9532 option = options.find("studio");
9533 if (option != options.end())
9535 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9536 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9539 option = options.find("directorid");
9540 if (option != options.end())
9542 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9543 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9546 option = options.find("year");
9547 if (option != options.end())
9548 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9550 option = options.find("actorid");
9551 if (option != options.end())
9553 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9554 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9557 option = options.find("actor");
9558 if (option != options.end())
9560 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9561 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9564 option = options.find("tagid");
9565 if (option != options.end())
9567 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9568 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9571 option = options.find("tag");
9572 if (option != options.end())
9574 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9575 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9578 else if (itemType == "seasons")
9580 option = options.find("genreid");
9581 if (option != options.end())
9583 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9584 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9587 option = options.find("directorid");
9588 if (option != options.end())
9590 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9591 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9594 option = options.find("year");
9595 if (option != options.end())
9596 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9598 option = options.find("actorid");
9599 if (option != options.end())
9601 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9602 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9605 else if (itemType == "episodes")
9608 option = options.find("tvshowid");
9609 if (option != options.end())
9610 idShow = (int)option->second.asInteger();
9613 option = options.find("season");
9614 if (option != options.end())
9615 season = (int)option->second.asInteger();
9617 CStdString strIn = PrepareSQL("= %i", idShow);
9618 GetStackedTvShowList(idShow, strIn);
9622 bool condition = false;
9624 option = options.find("genreid");
9625 if (option != options.end())
9628 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9629 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9632 option = options.find("genre");
9633 if (option != options.end())
9636 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9637 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9640 option = options.find("directorid");
9641 if (option != options.end())
9644 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9645 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9648 option = options.find("director");
9649 if (option != options.end())
9652 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9653 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9656 option = options.find("year");
9657 if (option != options.end())
9660 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9663 option = options.find("actorid");
9664 if (option != options.end())
9667 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9668 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9671 option = options.find("actor");
9672 if (option != options.end())
9675 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9676 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9680 filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9684 if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9685 filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9687 filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9688 VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9693 option = options.find("year");
9694 if (option != options.end())
9695 filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9697 option = options.find("directorid");
9698 if (option != options.end())
9700 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9701 filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9704 option = options.find("director");
9705 if (option != options.end())
9707 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9708 filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9713 else if (type == "musicvideos")
9715 option = options.find("genreid");
9716 if (option != options.end())
9718 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9719 filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9722 option = options.find("genre");
9723 if (option != options.end())
9725 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9726 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9729 option = options.find("studioid");
9730 if (option != options.end())
9732 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9733 filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9736 option = options.find("studio");
9737 if (option != options.end())
9739 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9740 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9743 option = options.find("directorid");
9744 if (option != options.end())
9746 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9747 filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9750 option = options.find("director");
9751 if (option != options.end())
9753 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9754 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9757 option = options.find("year");
9758 if (option != options.end())
9759 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9761 option = options.find("artistid");
9762 if (option != options.end())
9764 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9765 filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9768 option = options.find("artist");
9769 if (option != options.end())
9771 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9772 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9775 option = options.find("albumid");
9776 if (option != options.end())
9777 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = (select c%02d from musicvideo where idMVideo = %i)", VIDEODB_ID_MUSICVIDEO_ALBUM, VIDEODB_ID_MUSICVIDEO_ALBUM, (int)option->second.asInteger()));
9779 option = options.find("tagid");
9780 if (option != options.end())
9782 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9783 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9786 option = options.find("tag");
9787 if (option != options.end())
9789 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9790 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9796 option = options.find("xsp");
9797 if (option != options.end())
9800 if (!xsp.LoadFromJson(option->second.asString()))
9803 // check if the filter playlist matches the item type
9804 if (xsp.GetType() == itemType ||
9805 (xsp.GetGroup() == itemType && !xsp.IsGroupMixed()) ||
9806 // handle episode listings with videodb://tvshows/titles/ which get the rest
9807 // of the path (season and episodeid) appended later
9808 (xsp.GetType() == "episodes" && itemType == "tvshows"))
9810 std::set<CStdString> playlists;
9811 filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9813 if (xsp.GetLimit() > 0)
9814 sorting.limitEnd = xsp.GetLimit();
9815 if (xsp.GetOrder() != SortByNone)
9816 sorting.sortBy = xsp.GetOrder();
9817 if (xsp.GetOrderDirection() != SortOrderNone)
9818 sorting.sortOrder = xsp.GetOrderDirection();
9819 if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
9820 sorting.sortAttributes = SortAttributeIgnoreArticle;
9824 option = options.find("filter");
9825 if (option != options.end())
9827 CSmartPlaylist xspFilter;
9828 if (!xspFilter.LoadFromJson(option->second.asString()))
9831 // check if the filter playlist matches the item type
9832 if (xspFilter.GetType() == itemType)
9834 std::set<CStdString> playlists;
9835 filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9837 // remove the filter if it doesn't match the item type
9839 videoUrl.RemoveOption("filter");