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 strSQL = "update tvshow set ";
1655 for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1656 strSQL += StringUtils::Format("c%02d=NULL,", iType);
1658 strSQL = strSQL.Mid(0, strSQL.size() - 1) + PrepareSQL(" where idShow=%i", idTvShow);
1659 m_pDS->exec(strSQL.c_str());
1663 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1667 //********************************************************************************************************************************
1668 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
1671 filter.join = "LEFT JOIN actorlinkmovie ON actorlinkmovie.idMovie=movieview.idMovie "
1672 "LEFT JOIN actors a ON a.idActor=actorlinkmovie.idActor "
1673 "LEFT JOIN directorlinkmovie ON directorlinkmovie.idMovie=movieview.idMovie "
1674 "LEFT JOIN actors d ON d.idActor=directorlinkmovie.idDirector";
1675 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1676 filter.group = "movieview.idMovie";
1677 GetMoviesByWhere("videodb://movies/titles/", filter, items);
1680 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
1683 filter.join = "LEFT JOIN actorlinktvshow ON actorlinktvshow.idShow=tvshowview.idShow "
1684 "LEFT JOIN actors a ON a.idActor=actorlinktvshow.idActor "
1685 "LEFT JOIN directorlinktvshow ON directorlinktvshow.idShow=tvshowview.idShow "
1686 "LEFT JOIN actors d ON d.idActor=directorlinktvshow.idDirector";
1687 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1688 filter.group = "tvshowview.idShow";
1689 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
1692 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
1695 filter.join = "LEFT JOIN actorlinkepisode ON actorlinkepisode.idEpisode=episodeview.idEpisode "
1696 "LEFT JOIN actors a ON a.idActor=actorlinkepisode.idActor "
1697 "LEFT JOIN directorlinkepisode ON directorlinkepisode.idEpisode=episodeview.idEpisode "
1698 "LEFT JOIN actors d ON d.idActor=directorlinkepisode.idDirector";
1699 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1700 filter.group = "episodeview.idEpisode";
1701 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
1704 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1709 if (NULL == m_pDB.get()) return ;
1710 if (NULL == m_pDS.get()) return ;
1713 if (strArtist.empty()) // TODO: SMARTPLAYLISTS what is this here for???
1714 strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1716 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());
1717 m_pDS->query( strSQL.c_str() );
1719 while (!m_pDS->eof())
1721 CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1722 CFileItemPtr pItem(new CFileItem(tag));
1723 pItem->SetLabel(StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator));
1731 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1735 //********************************************************************************************************************************
1736 bool CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
1740 // TODO: Optimize this - no need for all the queries!
1742 idMovie = GetMovieId(strFilenameAndPath);
1743 if (idMovie < 0) return false;
1745 CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
1746 if (!m_pDS->query(sql.c_str()))
1748 details = GetDetailsForMovie(m_pDS, true);
1749 return !details.IsEmpty();
1753 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1758 //********************************************************************************************************************************
1759 bool CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
1764 idTvShow = GetTvShowId(strPath);
1765 if (idTvShow < 0) return false;
1767 CStdString sql = PrepareSQL("SELECT * FROM tvshowview WHERE idShow=%i", idTvShow);
1768 if (!m_pDS->query(sql.c_str()))
1770 details = GetDetailsForTvShow(m_pDS, true);
1771 return !details.IsEmpty();
1775 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1780 bool CVideoDatabase::GetSeasonInfo(int idSeason, CVideoInfoTag& details)
1787 if (!m_pDB.get() || !m_pDS.get())
1790 CStdString sql = PrepareSQL("SELECT idShow FROM seasons WHERE idSeason=%i", idSeason);
1791 if (!m_pDS->query(sql.c_str()))
1795 if (m_pDS->num_rows() == 1)
1796 idShow = m_pDS->fv(0).get_asInt();
1803 CFileItemList seasons;
1804 if (!GetSeasonsNav(StringUtils::Format("videodb://tvshows/titles/%ld/", idShow), seasons, -1, -1, -1, -1, idShow, false) || seasons.Size() <= 0)
1807 for (int index = 0; index < seasons.Size(); index++)
1809 const CFileItemPtr season = seasons.Get(index);
1810 if (season->HasVideoInfoTag() && season->GetVideoInfoTag()->m_iDbId == idSeason && season->GetVideoInfoTag()->m_iIdShow == idShow)
1812 details = *season->GetVideoInfoTag();
1819 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
1824 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
1828 // TODO: Optimize this - no need for all the queries!
1830 idEpisode = GetEpisodeId(strFilenameAndPath);
1831 if (idEpisode < 0) return false;
1833 CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
1834 if (!m_pDS->query(sql.c_str()))
1836 details = GetDetailsForEpisode(m_pDS, true);
1837 return !details.IsEmpty();
1841 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1846 bool CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
1850 // TODO: Optimize this - no need for all the queries!
1852 idMVideo = GetMusicVideoId(strFilenameAndPath);
1853 if (idMVideo < 0) return false;
1855 CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
1856 if (!m_pDS->query(sql.c_str()))
1858 details = GetDetailsForMusicVideo(m_pDS, true);
1859 return !details.IsEmpty();
1863 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1868 bool CVideoDatabase::GetSetInfo(int idSet, CVideoInfoTag& details)
1876 filter.where = PrepareSQL("sets.idSet=%d", idSet);
1877 CFileItemList items;
1878 if (!GetSetsByWhere("videodb://movies/sets/", filter, items) ||
1879 items.Size() != 1 ||
1880 !items[0]->HasVideoInfoTag())
1883 details = *(items[0]->GetVideoInfoTag());
1884 return !details.IsEmpty();
1888 CLog::Log(LOGERROR, "%s (%d) failed", __FUNCTION__, idSet);
1893 bool CVideoDatabase::GetFileInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idFile /* = -1 */)
1898 idFile = GetFileId(strFilenameAndPath);
1902 CStdString sql = PrepareSQL("SELECT * FROM files "
1903 "JOIN path ON path.idPath = files.idPath "
1904 "LEFT JOIN bookmark ON bookmark.idFile = files.idFile AND bookmark.type = %i "
1905 "WHERE files.idFile = %i", CBookmark::RESUME, idFile);
1906 if (!m_pDS->query(sql.c_str()))
1909 details.m_iFileId = m_pDS->fv("files.idFile").get_asInt();
1910 details.m_strPath = m_pDS->fv("path.strPath").get_asString();
1911 CStdString strFileName = m_pDS->fv("files.strFilename").get_asString();
1912 ConstructPath(details.m_strFileNameAndPath, details.m_strPath, strFileName);
1913 details.m_playCount = max(details.m_playCount, m_pDS->fv("files.playCount").get_asInt());
1914 if (!details.m_lastPlayed.IsValid())
1915 details.m_lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
1916 if (!details.m_dateAdded.IsValid())
1917 details.m_dateAdded.SetFromDBDateTime(m_pDS->fv("files.dateAdded").get_asString());
1918 if (!details.m_resumePoint.IsSet())
1920 details.m_resumePoint.timeInSeconds = m_pDS->fv("bookmark.timeInSeconds").get_asInt();
1921 details.m_resumePoint.totalTimeInSeconds = m_pDS->fv("bookmark.totalTimeInSeconds").get_asInt();
1922 details.m_resumePoint.type = CBookmark::RESUME;
1925 return !details.IsEmpty();
1929 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1934 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1936 // add all directors
1937 for (unsigned int i = 0; i < details.m_director.size(); i++)
1938 vecDirectors.push_back(AddActor(details.m_director[i],""));
1941 for (unsigned int i = 0; i < details.m_genre.size(); i++)
1942 vecGenres.push_back(AddGenre(details.m_genre[i]));
1944 for (unsigned int i = 0; i < details.m_studio.size(); i++)
1945 vecStudios.push_back(AddStudio(details.m_studio[i]));
1948 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1951 for (int i = min + 1; i < max; ++i)
1953 switch (offsets[i].type)
1955 case VIDEODB_TYPE_STRING:
1956 sql += PrepareSQL("c%02d='%s',", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str());
1958 case VIDEODB_TYPE_INT:
1959 sql += PrepareSQL("c%02d='%i',", i, *(int*)(((char*)&details)+offsets[i].offset));
1961 case VIDEODB_TYPE_COUNT:
1963 int value = *(int*)(((char*)&details)+offsets[i].offset);
1965 sql += PrepareSQL("c%02d=%i,", i, value);
1967 sql += PrepareSQL("c%02d=NULL,", i);
1970 case VIDEODB_TYPE_BOOL:
1971 sql += PrepareSQL("c%02d='%s',", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false");
1973 case VIDEODB_TYPE_FLOAT:
1974 sql += PrepareSQL("c%02d='%f',", i, *(float*)(((char*)&details)+offsets[i].offset));
1976 case VIDEODB_TYPE_STRINGARRAY:
1977 sql += PrepareSQL("c%02d='%s',", i, StringUtils::Join(*((std::vector<std::string>*)(((char*)&details)+offsets[i].offset)), g_advancedSettings.m_videoItemSeparator).c_str());
1979 case VIDEODB_TYPE_DATE:
1980 sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDate().c_str());
1982 case VIDEODB_TYPE_DATETIME:
1983 sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDateTime().c_str());
1991 //********************************************************************************************************************************
1992 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMovie /* = -1 */)
1999 idMovie = GetMovieId(strFilenameAndPath);
2002 DeleteMovie(strFilenameAndPath, true, idMovie); // true to keep the table entry
2005 // only add a new movie if we don't already have a valid idMovie
2006 // (DeleteMovie is called with bKeepId == true so the movie won't
2007 // be removed from the movie table)
2008 idMovie = AddMovie(strFilenameAndPath);
2011 RollbackTransaction();
2016 vector<int> vecDirectors;
2017 vector<int> vecGenres;
2018 vector<int> vecStudios;
2019 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2021 for (unsigned int i = 0; i < vecGenres.size(); ++i)
2022 AddGenreToMovie(idMovie, vecGenres[i]);
2024 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2025 AddDirectorToMovie(idMovie, vecDirectors[i]);
2027 for (unsigned int i = 0; i < vecStudios.size(); ++i)
2028 AddStudioToMovie(idMovie, vecStudios[i]);
2031 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2032 AddWriterToMovie(idMovie, AddActor(details.m_writingCredits[i],""));
2034 AddCast(idMovie, "movie", "movie", details.m_cast);
2038 if (!details.m_strSet.empty())
2040 idSet = AddSet(details.m_strSet);
2041 // add art if not available
2042 map<string, string> setArt;
2043 if (!GetArtForItem(idSet, "set", setArt))
2044 SetArtForItem(idSet, "set", artwork);
2048 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2050 int idTag = AddTag(details.m_tags[i]);
2051 AddTagToItem(idMovie, idTag, "movie");
2055 for (unsigned int i = 0; i < details.m_country.size(); i++)
2056 AddCountryToMovie(idMovie, AddCountry(details.m_country[i]));
2058 if (details.HasStreamDetails())
2059 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2061 SetArtForItem(idMovie, "movie", artwork);
2063 // query DB for any movies matching imdbid and year
2064 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);
2065 m_pDS->query(strSQL.c_str());
2069 int playCount = m_pDS->fv("files.playCount").get_asInt();
2071 CDateTime lastPlayed;
2072 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2074 int idFile = GetFileId(strFilenameAndPath);
2076 // update with playCount and lastPlayed
2077 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2078 m_pDS->exec(strSQL.c_str());
2083 // update our movie table (we know it was added already above)
2084 // and insert the new row
2085 CStdString sql = "update movie set " + GetValueString(details, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
2087 sql += PrepareSQL(", idSet = %i", idSet);
2089 sql += ", idSet = NULL";
2090 sql += PrepareSQL(" where idMovie=%i", idMovie);
2091 m_pDS->exec(sql.c_str());
2092 CommitTransaction();
2098 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2100 RollbackTransaction();
2104 int CVideoDatabase::SetDetailsForMovieSet(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idSet /* = -1 */)
2106 if (details.m_strTitle.empty())
2114 idSet = AddSet(details.m_strTitle);
2117 RollbackTransaction();
2122 SetArtForItem(idSet, "set", artwork);
2124 // and insert the new row
2125 CStdString sql = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", details.m_strTitle.c_str(), idSet);
2126 m_pDS->exec(sql.c_str());
2127 CommitTransaction();
2133 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
2135 RollbackTransaction();
2139 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, map<string, string> > &seasonArt, int idTvShow /*= -1 */)
2143 if (!m_pDB.get() || !m_pDS.get())
2145 CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
2152 idTvShow = GetTvShowId(strPath);
2155 DeleteDetailsForTvShow(strPath, idTvShow);
2158 idTvShow = AddTvShow(strPath);
2161 RollbackTransaction();
2166 vector<int> vecDirectors;
2167 vector<int> vecGenres;
2168 vector<int> vecStudios;
2169 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2171 AddCast(idTvShow, "tvshow", "show", details.m_cast);
2174 for (i = 0; i < vecGenres.size(); ++i)
2175 AddGenreToTvShow(idTvShow, vecGenres[i]);
2177 for (i = 0; i < vecDirectors.size(); ++i)
2178 AddDirectorToTvShow(idTvShow, vecDirectors[i]);
2180 for (i = 0; i < vecStudios.size(); ++i)
2181 AddStudioToTvShow(idTvShow, vecStudios[i]);
2184 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2186 int idTag = AddTag(details.m_tags[i]);
2187 AddTagToItem(idTvShow, idTag, "tvshow");
2190 // add "all seasons" - the rest are added in SetDetailsForEpisode
2191 AddSeason(idTvShow, -1);
2193 SetArtForItem(idTvShow, "tvshow", artwork);
2194 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
2196 int idSeason = AddSeason(idTvShow, i->first);
2198 SetArtForItem(idSeason, "season", i->second);
2201 // and insert the new row
2202 CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
2203 sql += PrepareSQL(" where idShow=%i", idTvShow);
2204 m_pDS->exec(sql.c_str());
2206 CommitTransaction();
2212 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2214 RollbackTransaction();
2218 int CVideoDatabase::SetDetailsForSeason(const CVideoInfoTag& details, const std::map<std::string, std::string> &artwork, int idShow, int idSeason /* = -1 */)
2220 if (idShow < 0 || details.m_iSeason < 0)
2228 idSeason = AddSeason(idShow, details.m_iSeason);
2231 RollbackTransaction();
2236 SetArtForItem(idSeason, "season", artwork);
2238 // and insert the new row
2239 CStdString sql = PrepareSQL("UPDATE seasons SET season=%i WHERE idSeason=%i", details.m_iSeason, idSeason);
2240 m_pDS->exec(sql.c_str());
2241 CommitTransaction();
2247 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSeason);
2249 RollbackTransaction();
2253 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idShow, int idEpisode)
2259 idEpisode = GetEpisodeId(strFilenameAndPath);
2262 DeleteEpisode(strFilenameAndPath, idEpisode, true); // true to keep the table entry
2265 // only add a new episode if we don't already have a valid idEpisode
2266 // (DeleteEpisode is called with bKeepId == true so the episode won't
2267 // be removed from the episode table)
2268 idEpisode = AddEpisode(idShow,strFilenameAndPath);
2271 RollbackTransaction();
2276 vector<int> vecDirectors;
2277 vector<int> vecGenres;
2278 vector<int> vecStudios;
2279 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2281 AddCast(idEpisode, "episode", "episode", details.m_cast);
2284 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2285 AddWriterToEpisode(idEpisode, AddActor(details.m_writingCredits[i],""));
2287 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2289 AddDirectorToEpisode(idEpisode, vecDirectors[i]);
2292 if (details.HasStreamDetails())
2294 if (details.m_iFileId != -1)
2295 SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2297 SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
2300 // ensure we have this season already added
2301 AddSeason(idShow, details.m_iSeason);
2303 SetArtForItem(idEpisode, "episode", artwork);
2305 // query DB for any episodes matching idShow, Season and Episode
2306 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);
2307 m_pDS->query(strSQL.c_str());
2311 int playCount = m_pDS->fv("files.playCount").get_asInt();
2313 CDateTime lastPlayed;
2314 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2316 int idFile = GetFileId(strFilenameAndPath);
2318 // update with playCount and lastPlayed
2319 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2320 m_pDS->exec(strSQL.c_str());
2325 // and insert the new row
2326 CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
2327 sql += PrepareSQL(" where idEpisode=%i", idEpisode);
2328 m_pDS->exec(sql.c_str());
2329 CommitTransaction();
2335 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2337 RollbackTransaction();
2341 int CVideoDatabase::GetSeasonId(int showID, int season)
2343 CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
2344 CStdString id = GetSingleValue("seasons", "idSeason", sql);
2347 return strtol(id.c_str(), NULL, 10);
2350 int CVideoDatabase::AddSeason(int showID, int season)
2352 int seasonId = GetSeasonId(showID, season);
2355 if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
2356 seasonId = (int)m_pDS->lastinsertid();
2361 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
2368 idMVideo = GetMusicVideoId(strFilenameAndPath);
2371 DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
2374 // only add a new musicvideo if we don't already have a valid idMVideo
2375 // (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
2376 // be removed from the musicvideo table)
2377 idMVideo = AddMusicVideo(strFilenameAndPath);
2380 RollbackTransaction();
2385 vector<int> vecDirectors;
2386 vector<int> vecGenres;
2387 vector<int> vecStudios;
2388 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2391 if (!details.m_artist.empty())
2393 for (unsigned int i = 0; i < details.m_artist.size(); i++)
2395 CStdString artist = details.m_artist[i];
2397 int idArtist = AddActor(artist,"");
2398 AddArtistToMusicVideo(idMVideo, idArtist);
2403 for (i = 0; i < vecGenres.size(); ++i)
2405 AddGenreToMusicVideo(idMVideo, vecGenres[i]);
2408 for (i = 0; i < vecDirectors.size(); ++i)
2410 AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
2413 for (i = 0; i < vecStudios.size(); ++i)
2415 AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2419 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2421 int idTag = AddTag(details.m_tags[i]);
2422 AddTagToItem(idMVideo, idTag, "musicvideo");
2425 if (details.HasStreamDetails())
2426 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2428 SetArtForItem(idMVideo, "musicvideo", artwork);
2430 // update our movie table (we know it was added already above)
2431 // and insert the new row
2432 CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
2433 sql += PrepareSQL(" where idMVideo=%i", idMVideo);
2434 m_pDS->exec(sql.c_str());
2435 CommitTransaction();
2441 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2443 RollbackTransaction();
2447 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2449 // AddFile checks to make sure the file isn't already in the DB first
2450 int idFile = AddFile(strFileNameAndPath);
2453 SetStreamDetailsForFileId(details, idFile);
2456 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2464 m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2466 for (int i=1; i<=details.GetVideoStreamCount(); i++)
2468 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2469 "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration, strStereoMode) "
2470 "VALUES (%i,%i,'%s',%f,%i,%i,%i,'%s')",
2471 idFile, (int)CStreamDetail::VIDEO,
2472 details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2473 details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i),
2474 details.GetStereoMode(i).c_str()));
2476 for (int i=1; i<=details.GetAudioStreamCount(); i++)
2478 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2479 "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2480 "VALUES (%i,%i,'%s',%i,'%s')",
2481 idFile, (int)CStreamDetail::AUDIO,
2482 details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2483 details.GetAudioLanguage(i).c_str()));
2485 for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2487 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2488 "(idFile, iStreamType, strSubtitleLanguage) "
2489 "VALUES (%i,%i,'%s')",
2490 idFile, (int)CStreamDetail::SUBTITLE,
2491 details.GetSubtitleLanguage(i).c_str()));
2494 // update the runtime information, if empty
2495 if (details.GetVideoDuration())
2497 vector< pair<string, int> > tables;
2498 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
2499 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
2500 tables.push_back(make_pair("musicvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
2501 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
2503 CStdString sql = PrepareSQL("update %s set c%02d=%d where idFile=%d and c%02d=''",
2504 i->first.c_str(), i->second, details.GetVideoDuration(), idFile, i->second);
2509 CommitTransaction();
2513 RollbackTransaction();
2514 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2518 //********************************************************************************************************************************
2519 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2523 if (NULL == m_pDB.get()) return ;
2524 if (NULL == m_pDS.get()) return ;
2526 if (idMovie < 0) return ;
2529 if (iType == VIDEODB_CONTENT_MOVIES)
2530 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 );
2531 if (iType == VIDEODB_CONTENT_EPISODES)
2532 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 );
2533 if (iType == VIDEODB_CONTENT_TVSHOWS)
2534 strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2535 if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2536 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 );
2538 m_pDS->query( strSQL.c_str() );
2541 if (iType != VIDEODB_CONTENT_TVSHOWS)
2543 CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2544 ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2547 filePath = m_pDS->fv("path.strPath").get_asString();
2553 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2557 //********************************************************************************************************************************
2558 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend, long partNumber)
2562 if (URIUtils::IsStack(strFilenameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(strFilenameAndPath),false).IsDVDImage())
2564 CStackDirectory dir;
2565 CFileItemList fileList;
2566 dir.GetDirectory(strFilenameAndPath, fileList);
2569 for (int i = fileList.Size() - 1; i >= 0; i--) // put the bookmarks of the highest part first in the list
2570 GetBookMarksForFile(fileList[i]->GetPath(), bookmarks, type, true, (i+1));
2574 int idFile = GetFileId(strFilenameAndPath);
2575 if (idFile < 0) return ;
2577 bookmarks.erase(bookmarks.begin(), bookmarks.end());
2578 if (NULL == m_pDB.get()) return ;
2579 if (NULL == m_pDS.get()) return ;
2581 CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2582 m_pDS->query( strSQL.c_str() );
2583 while (!m_pDS->eof())
2586 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2587 bookmark.partNumber = partNumber;
2588 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2589 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2590 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2591 bookmark.player = m_pDS->fv("player").get_asString();
2592 bookmark.type = type;
2593 if (type == CBookmark::EPISODE)
2595 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);
2596 m_pDS2->query(strSQL2.c_str());
2597 bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2598 bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2601 bookmarks.push_back(bookmark);
2604 //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2610 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2614 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2616 VECBOOKMARKS bookmarks;
2617 GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2618 if (bookmarks.size() > 0)
2620 bookmark = bookmarks[0];
2626 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2628 if (!m_pDB.get() || !m_pDS.get())
2631 int fileID = GetFileId(strFilenameAndPath);
2637 CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2638 m_pDS->exec(sql.c_str());
2642 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2646 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2650 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);
2651 m_pDS->query(strSQL.c_str());
2652 while (!m_pDS->eof())
2654 episodes.push_back(GetDetailsForEpisode(m_pDS));
2661 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2665 //********************************************************************************************************************************
2666 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2670 int idFile = AddFile(strFilenameAndPath);
2673 if (NULL == m_pDB.get()) return ;
2674 if (NULL == m_pDS.get()) return ;
2678 if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2680 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2682 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
2684 /* get a bookmark within the same time as previous */
2685 double mintime = bookmark.timeInSeconds - 0.5f;
2686 double maxtime = bookmark.timeInSeconds + 0.5f;
2687 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());
2690 if (type != CBookmark::EPISODE)
2693 m_pDS->query( strSQL.c_str() );
2694 if (m_pDS->num_rows() != 0)
2695 idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2698 // update or insert depending if it existed before
2699 if (idBookmark >= 0 )
2700 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);
2702 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);
2704 m_pDS->exec(strSQL.c_str());
2708 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2712 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2716 int idFile = GetFileId(strFilenameAndPath);
2717 if (idFile < 0) return ;
2718 if (NULL == m_pDB.get()) return ;
2719 if (NULL == m_pDS.get()) return ;
2721 /* a litle bit uggly, we clear first bookmark that is within one second of given */
2722 /* should be no problem since we never add bookmarks that are closer than that */
2723 double mintime = bookmark.timeInSeconds - 0.5f;
2724 double maxtime = bookmark.timeInSeconds + 0.5f;
2725 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);
2727 m_pDS->query( strSQL.c_str() );
2728 if (m_pDS->num_rows() != 0)
2730 int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2731 strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2732 m_pDS->exec(strSQL.c_str());
2733 if (type == CBookmark::EPISODE)
2735 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);
2736 m_pDS->exec(strSQL.c_str());
2744 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2748 //********************************************************************************************************************************
2749 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2753 int idFile = GetFileId(strFilenameAndPath);
2754 if (idFile < 0) return ;
2755 if (NULL == m_pDB.get()) return ;
2756 if (NULL == m_pDS.get()) return ;
2758 CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2759 m_pDS->exec(strSQL.c_str());
2760 if (type == CBookmark::EPISODE)
2762 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2763 m_pDS->exec(strSQL.c_str());
2768 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2773 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2777 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);
2778 m_pDS->query( strSQL.c_str() );
2781 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2782 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2783 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2784 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2785 bookmark.player = m_pDS->fv("player").get_asString();
2786 bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2797 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2803 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2807 int idFile = GetFileId(tag.m_strFileNameAndPath);
2808 // delete the current episode for the selected episode number
2809 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);
2810 m_pDS->exec(strSQL.c_str());
2812 AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2813 int idBookmark = (int)m_pDS->lastinsertid();
2814 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);
2815 m_pDS->exec(strSQL.c_str());
2819 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2823 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2827 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2828 m_pDS->exec(strSQL.c_str());
2829 strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2830 m_pDS->exec(strSQL.c_str());
2834 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2838 //********************************************************************************************************************************
2839 void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
2845 GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
2847 DeleteMovie(path, bKeepId, idMovie);
2850 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
2854 if (NULL == m_pDB.get()) return ;
2855 if (NULL == m_pDS.get()) return ;
2858 idMovie = GetMovieId(strFilenameAndPath);
2866 strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2867 m_pDS->exec(strSQL.c_str());
2869 strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2870 m_pDS->exec(strSQL.c_str());
2872 strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2873 m_pDS->exec(strSQL.c_str());
2875 strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2876 m_pDS->exec(strSQL.c_str());
2878 strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2879 m_pDS->exec(strSQL.c_str());
2881 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2883 // keep the movie table entry, linking to tv shows, and bookmarks
2884 // so we can update the data in place
2885 // the ancilliary tables are still purged
2888 ClearBookMarksOfFile(strFilenameAndPath);
2890 strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2891 m_pDS->exec(strSQL.c_str());
2893 strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2894 m_pDS->exec(strSQL.c_str());
2897 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2899 AnnounceRemove("movie", idMovie);
2901 CStdString strPath, strFileName;
2902 SplitPath(strFilenameAndPath,strPath,strFileName);
2903 InvalidatePathHash(strPath);
2904 CommitTransaction();
2909 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2910 RollbackTransaction();
2914 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2920 GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2922 DeleteTvShow(path, bKeepId, idTvShow);
2925 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2929 if (NULL == m_pDB.get()) return ;
2930 if (NULL == m_pDS.get()) return ;
2933 idTvShow = GetTvShowId(strPath);
2940 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);
2941 m_pDS2->query(strSQL.c_str());
2942 while (!m_pDS2->eof())
2944 CStdString strFilenameAndPath;
2945 CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2946 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2947 ConstructPath(strFilenameAndPath, strPath, strFileName);
2948 DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2952 DeleteDetailsForTvShow(strPath, idTvShow);
2954 strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2955 m_pDS->exec(strSQL.c_str());
2957 // keep tvshow table and movielink table so we can update data in place
2960 strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2961 m_pDS->exec(strSQL.c_str());
2963 strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2964 m_pDS->exec(strSQL.c_str());
2966 strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2967 m_pDS->exec(strSQL.c_str());
2970 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2972 AnnounceRemove("tvshow", idTvShow);
2974 InvalidatePathHash(strPath);
2976 CommitTransaction();
2981 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2982 RollbackTransaction();
2986 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2992 GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2994 DeleteEpisode(path, idEpisode, bKeepId);
2997 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
3001 if (NULL == m_pDB.get()) return ;
3002 if (NULL == m_pDS.get()) return ;
3005 idEpisode = GetEpisodeId(strFilenameAndPath);
3012 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3014 AnnounceRemove("episode", idEpisode);
3017 strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
3018 m_pDS->exec(strSQL.c_str());
3020 strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
3021 m_pDS->exec(strSQL.c_str());
3023 strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
3024 m_pDS->exec(strSQL.c_str());
3026 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3028 // keep episode table entry and bookmarks so we can update the data in place
3029 // the ancilliary tables are still purged
3032 ClearBookMarksOfFile(strFilenameAndPath);
3034 strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
3035 m_pDS->exec(strSQL.c_str());
3041 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3045 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
3047 if (idMusicVideo < 0)
3051 GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
3053 DeleteMusicVideo(path, bKeepId, idMusicVideo);
3056 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
3060 if (NULL == m_pDB.get()) return ;
3061 if (NULL == m_pDS.get()) return ;
3064 idMVideo = GetMusicVideoId(strFilenameAndPath);
3072 strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
3073 m_pDS->exec(strSQL.c_str());
3075 strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
3076 m_pDS->exec(strSQL.c_str());
3078 strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
3079 m_pDS->exec(strSQL.c_str());
3081 strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
3082 m_pDS->exec(strSQL.c_str());
3084 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3086 // keep the music video table entry and bookmarks so we can update data in place
3087 // the ancilliary tables are still purged
3090 ClearBookMarksOfFile(strFilenameAndPath);
3092 strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
3093 m_pDS->exec(strSQL.c_str());
3096 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3098 AnnounceRemove("musicvideo", idMVideo);
3100 CStdString strPath, strFileName;
3101 SplitPath(strFilenameAndPath,strPath,strFileName);
3102 InvalidatePathHash(strPath);
3103 CommitTransaction();
3108 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3109 RollbackTransaction();
3113 void CVideoDatabase::DeleteStreamDetails(int idFile)
3115 m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
3118 void CVideoDatabase::DeleteSet(int idSet)
3122 if (NULL == m_pDB.get()) return ;
3123 if (NULL == m_pDS.get()) return ;
3126 strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
3127 m_pDS->exec(strSQL.c_str());
3128 strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3129 m_pDS->exec(strSQL.c_str());
3133 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3137 void CVideoDatabase::ClearMovieSet(int idMovie)
3139 SetMovieSet(idMovie, -1);
3142 void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
3145 ExecuteQuery(PrepareSQL("update movie set idSet = %i where idMovie = %i", idSet, idMovie));
3147 ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
3150 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3154 if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3158 if (mediaType == VIDEODB_CONTENT_MOVIES)
3160 else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3162 else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3163 type = "musicvideo";
3168 strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3169 m_pDS->exec(strSQL.c_str());
3173 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3177 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3179 GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3182 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3184 for (int i = min + 1; i < max; i++)
3186 switch (offsets[i].type)
3188 case VIDEODB_TYPE_STRING:
3189 *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3191 case VIDEODB_TYPE_INT:
3192 case VIDEODB_TYPE_COUNT:
3193 *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3195 case VIDEODB_TYPE_BOOL:
3196 *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3198 case VIDEODB_TYPE_FLOAT:
3199 *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3201 case VIDEODB_TYPE_STRINGARRAY:
3202 *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3204 case VIDEODB_TYPE_DATE:
3205 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3207 case VIDEODB_TYPE_DATETIME:
3208 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3214 DWORD movieTime = 0;
3217 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3219 CVideoInfoTag details;
3224 case VIDEODB_CONTENT_MOVIES:
3225 GetMovieInfo("", details, id);
3227 case VIDEODB_CONTENT_TVSHOWS:
3228 GetTvShowInfo("", details, id);
3230 case VIDEODB_CONTENT_EPISODES:
3231 GetEpisodeInfo("", details, id);
3233 case VIDEODB_CONTENT_MUSICVIDEOS:
3234 GetMusicVideoInfo("", details, id);
3243 bool CVideoDatabase::GetStreamDetails(CFileItem& item)
3245 // Note that this function (possibly) creates VideoInfoTags for items that don't have one yet!
3248 if (item.HasVideoInfoTag())
3249 fileId = item.GetVideoInfoTag()->m_iFileId;
3252 fileId = GetFileId(item);
3257 // Have a file id, get stream details if available (creates tag either way)
3258 item.GetVideoInfoTag()->m_iFileId = fileId;
3259 return GetStreamDetails(*item.GetVideoInfoTag());
3262 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3264 if (tag.m_iFileId < 0)
3267 bool retVal = false;
3269 CStreamDetails& details = tag.m_streamDetails;
3272 auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3275 CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3280 CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3283 case CStreamDetail::VIDEO:
3285 CStreamDetailVideo *p = new CStreamDetailVideo();
3286 p->m_strCodec = pDS->fv(2).get_asString();
3287 p->m_fAspect = pDS->fv(3).get_asFloat();
3288 p->m_iWidth = pDS->fv(4).get_asInt();
3289 p->m_iHeight = pDS->fv(5).get_asInt();
3290 p->m_iDuration = pDS->fv(10).get_asInt();
3291 p->m_strStereoMode = pDS->fv(11).get_asString();
3292 details.AddStream(p);
3296 case CStreamDetail::AUDIO:
3298 CStreamDetailAudio *p = new CStreamDetailAudio();
3299 p->m_strCodec = pDS->fv(6).get_asString();
3300 if (pDS->fv(7).get_isNull())
3301 p->m_iChannels = -1;
3303 p->m_iChannels = pDS->fv(7).get_asInt();
3304 p->m_strLanguage = pDS->fv(8).get_asString();
3305 details.AddStream(p);
3309 case CStreamDetail::SUBTITLE:
3311 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3312 p->m_strLanguage = pDS->fv(9).get_asString();
3313 details.AddStream(p);
3326 CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3328 details.DetermineBestStreams();
3330 if (details.GetVideoDuration() > 0)
3331 tag.m_duration = details.GetVideoDuration();
3336 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3338 if (tag.m_iFileId < 0)
3345 if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3347 CStackDirectory dir;
3348 CFileItemList fileList;
3349 dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3350 tag.m_resumePoint.Reset();
3351 for (int i = fileList.Size() - 1; i >= 0; i--)
3354 if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3356 tag.m_resumePoint = bookmark;
3357 tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3365 CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3366 m_pDS2->query( strSQL.c_str() );
3369 tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3370 tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3371 tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3372 tag.m_resumePoint.type = CBookmark::RESUME;
3380 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3386 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3388 return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3391 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3393 CVideoInfoTag details;
3398 DWORD time = XbmcThreads::SystemClockMillis();
3399 int idMovie = record->at(0).get_asInt();
3401 GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3403 details.m_iDbId = idMovie;
3404 details.m_type = "movie";
3406 details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3407 details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3408 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3409 details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3410 CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3411 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3412 details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3413 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3414 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3415 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3416 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3417 details.m_resumePoint.type = CBookmark::RESUME;
3419 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3423 GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3425 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3426 details.m_strPictureURL.Parse();
3429 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);
3430 m_pDS2->query(strSQL.c_str());
3431 while (!m_pDS2->eof())
3433 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3437 // create tvshowlink string
3439 GetLinksToTvShow(idMovie,links);
3440 for (unsigned int i=0;i<links.size();++i)
3442 CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3443 VIDEODB_ID_TV_TITLE,links[i]);
3444 m_pDS2->query(strSQL.c_str());
3446 details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3450 // get streamdetails
3451 GetStreamDetails(details);
3456 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3458 return GetDetailsForTvShow(pDS->get_sql_record(), getDetails);
3461 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3463 CVideoInfoTag details;
3468 DWORD time = XbmcThreads::SystemClockMillis();
3469 int idTvShow = record->at(0).get_asInt();
3471 GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3472 details.m_iDbId = idTvShow;
3473 details.m_type = "tvshow";
3474 details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3475 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3476 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3477 details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3478 details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3479 details.m_strShowPath = details.m_strPath;
3480 details.m_strShowTitle = details.m_strTitle;
3482 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3486 GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3489 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);
3490 m_pDS2->query(strSQL.c_str());
3491 while (!m_pDS2->eof())
3493 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3497 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3498 details.m_strPictureURL.Parse();
3503 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3505 return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3508 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3510 CVideoInfoTag details;
3515 DWORD time = XbmcThreads::SystemClockMillis();
3516 int idEpisode = record->at(0).get_asInt();
3518 GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3519 details.m_iDbId = idEpisode;
3520 details.m_type = "episode";
3521 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3522 details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3523 CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3524 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3525 details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3526 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3527 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3528 details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3529 details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3530 details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3531 details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3532 details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3533 details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3534 details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3536 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3537 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3538 details.m_resumePoint.type = CBookmark::RESUME;
3540 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3544 GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3545 GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3547 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3548 details.m_strPictureURL.Parse();
3549 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);
3550 m_pDS2->query(strSQL.c_str());
3552 details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3555 // get streamdetails
3556 GetStreamDetails(details);
3561 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3563 return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3566 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3568 CVideoInfoTag details;
3570 unsigned int time = XbmcThreads::SystemClockMillis();
3571 int idMVideo = record->at(0).get_asInt();
3573 GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3574 details.m_iDbId = idMVideo;
3575 details.m_type = "musicvideo";
3577 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3578 details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3579 CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3580 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3581 details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3582 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3583 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3584 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3585 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3586 details.m_resumePoint.type = CBookmark::RESUME;
3588 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3593 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);
3594 m_pDS2->query(strSQL.c_str());
3595 while (!m_pDS2->eof())
3597 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3602 details.m_strPictureURL.Parse();
3604 // get streamdetails
3605 GetStreamDetails(details);
3610 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3614 if (!m_pDB.get()) return;
3615 if (!m_pDS2.get()) return;
3617 CStdString sql = PrepareSQL("SELECT actors.strActor,"
3618 " actorlink%s.strRole,"
3619 " actorlink%s.iOrder,"
3624 " actorlink%s.idActor=actors.idActor"
3626 " art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3627 "WHERE actorlink%s.%s=%i "
3628 "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());
3629 m_pDS2->query(sql.c_str());
3630 while (!m_pDS2->eof())
3633 info.strName = m_pDS2->fv(0).get_asString();
3635 for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3637 if (i->strName == info.strName)
3645 info.strRole = m_pDS2->fv(1).get_asString();
3646 info.order = m_pDS2->fv(2).get_asInt();
3647 info.thumbUrl.ParseString(m_pDS2->fv(3).get_asString());
3648 info.thumb = m_pDS2->fv(4).get_asString();
3649 cast.push_back(info);
3657 CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3661 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3662 /// \retval Returns true if the settings exist, false otherwise.
3663 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3667 // obtain the FileID (if it exists)
3668 #ifdef NEW_VIDEODB_METHODS
3669 if (NULL == m_pDB.get()) return false;
3670 if (NULL == m_pDS.get()) return false;
3671 CStdString strPath, strFileName;
3672 URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3673 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());
3675 int idFile = GetFileId(strFilenameAndPath);
3676 if (idFile < 0) return false;
3677 if (NULL == m_pDB.get()) return false;
3678 if (NULL == m_pDS.get()) return false;
3679 // ok, now obtain the settings for this file
3680 CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3682 m_pDS->query( strSQL.c_str() );
3683 if (m_pDS->num_rows() > 0)
3684 { // get the video settings info
3685 settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3686 settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3687 settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3688 settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3689 settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3690 settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3691 settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3692 settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3693 settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3694 settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3695 settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3696 settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3697 settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3698 settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3699 settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3700 settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3701 settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3702 settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3703 settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3704 settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3705 settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3706 settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3707 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3708 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3709 settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3710 settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3711 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3712 settings.m_StereoMode = m_pDS->fv("StereoMode").get_asInt();
3713 settings.m_StereoInvert = m_pDS->fv("StereoInvert").get_asBool();
3714 settings.m_SubtitleCached = false;
3722 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3727 /// \brief Sets the settings for a particular video file
3728 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3732 if (NULL == m_pDB.get()) return ;
3733 if (NULL == m_pDS.get()) return ;
3734 int idFile = AddFile(strFilenameAndPath);
3737 CStdString strSQL = StringUtils::Format("select * from settings where idFile=%i", idFile);
3738 m_pDS->query( strSQL.c_str() );
3739 if (m_pDS->num_rows() > 0)
3743 strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3744 "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3745 "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3746 "DeinterlaceMode=%i,",
3747 setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3748 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3749 setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3750 setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3751 setting.m_DeinterlaceMode);
3753 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);
3755 m_pDS->exec(strSQL.c_str());
3761 strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3762 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3763 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3764 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3765 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode,StereoMode,StereoInvert) "
3767 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)",
3768 idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3769 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3770 setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3771 setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3772 setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3773 setting.m_DeinterlaceMode, setting.m_StereoMode, setting.m_StereoInvert);
3774 m_pDS->exec(strSQL.c_str());
3779 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3783 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3785 for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3786 SetArtForItem(mediaId, mediaType, i->first, i->second);
3789 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3793 if (NULL == m_pDB.get()) return;
3794 if (NULL == m_pDS.get()) return;
3796 // don't set <foo>.<bar> art types - these are derivative types from parent items
3797 if (artType.find('.') != string::npos)
3800 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());
3801 m_pDS->query(sql.c_str());
3804 int artId = m_pDS->fv(0).get_asInt();
3806 sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3807 m_pDS->exec(sql.c_str());
3812 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());
3813 m_pDS->exec(sql.c_str());
3818 CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3822 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3826 if (NULL == m_pDB.get()) return false;
3827 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3829 CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3830 m_pDS2->query(sql.c_str());
3831 while (!m_pDS2->eof())
3833 art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3837 return !art.empty();
3841 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3846 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3848 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());
3849 return GetSingleValue(query, m_pDS2);
3852 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::string &artType)
3854 return ExecuteQuery(PrepareSQL("DELETE FROM art WHERE media_id=%i AND media_type='%s' AND type='%s'", mediaId, mediaType.c_str(), artType.c_str()));
3857 bool CVideoDatabase::RemoveArtForItem(int mediaId, const std::string &mediaType, const std::set<std::string> &artTypes)
3860 for (set<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
3861 result &= RemoveArtForItem(mediaId, mediaType, *i);
3866 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3870 if (NULL == m_pDB.get()) return false;
3871 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3873 // get all seasons for this show
3874 CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3875 m_pDS2->query(sql.c_str());
3877 vector< pair<int, int> > seasons;
3878 while (!m_pDS2->eof())
3880 seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3885 for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3887 map<string, string> art;
3888 GetArtForItem(i->first, "season", art);
3889 seasonArt.insert(make_pair(i->second,art));
3895 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3900 bool CVideoDatabase::GetArtTypes(const std::string &mediaType, std::vector<std::string> &artTypes)
3904 if (NULL == m_pDB.get()) return false;
3905 if (NULL == m_pDS.get()) return false;
3907 CStdString sql = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str());
3908 int numRows = RunQuery(sql);
3910 return numRows == 0;
3912 while (!m_pDS->eof())
3914 artTypes.push_back(m_pDS->fv(0).get_asString());
3922 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str());
3927 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3928 /// \retval Returns true if the stack times exist, false otherwise.
3929 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> ×)
3933 // obtain the FileID (if it exists)
3934 int idFile = GetFileId(filePath);
3935 if (idFile < 0) return false;
3936 if (NULL == m_pDB.get()) return false;
3937 if (NULL == m_pDS.get()) return false;
3938 // ok, now obtain the settings for this file
3939 CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3940 m_pDS->query( strSQL.c_str() );
3941 if (m_pDS->num_rows() > 0)
3942 { // get the video settings info
3943 CStdStringArray timeString;
3945 StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3947 for (unsigned int i = 0; i < timeString.size(); i++)
3949 times.push_back(atoi(timeString[i].c_str()));
3950 timeTotal += atoi(timeString[i].c_str());
3953 return (timeTotal > 0);
3959 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3964 /// \brief Sets the stack times for a particular video file
3965 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> ×)
3969 if (NULL == m_pDB.get()) return ;
3970 if (NULL == m_pDS.get()) return ;
3971 int idFile = AddFile(filePath);
3975 // delete any existing items
3976 m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3979 CStdString timeString = StringUtils::Format("%i", times[0]);
3980 for (unsigned int i = 1; i < times.size(); i++)
3981 timeString += StringUtils::Format(",%i", times[i]);
3983 m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3987 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3991 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
3993 if(URIUtils::IsMultiPath(strPath))
3995 vector<CStdString> paths;
3996 CMultiPathDirectory::GetPaths(strPath, paths);
3998 for(unsigned i=0;i<paths.size();i++)
3999 RemoveContentForPath(paths[i], progress);
4004 if (NULL == m_pDB.get()) return ;
4005 if (NULL == m_pDS.get()) return ;
4009 progress->SetHeading(700);
4010 progress->SetLine(0, "");
4011 progress->SetLine(1, 313);
4012 progress->SetLine(2, 330);
4013 progress->SetPercentage(0);
4014 progress->StartModal();
4015 progress->ShowProgressBar(true);
4017 vector< pair<int,string> > paths;
4018 GetSubPaths(strPath, paths);
4020 for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
4022 bool bMvidsChecked=false;
4025 progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
4026 progress->Progress();
4029 if (HasTvShowInfo(i->second))
4030 DeleteTvShow(i->second);
4033 CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
4034 m_pDS2->query(strSQL.c_str());
4037 strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
4038 m_pDS2->query(strSQL.c_str());
4039 bMvidsChecked = true;
4041 while (!m_pDS2->eof())
4043 CStdString strMoviePath;
4044 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
4045 ConstructPath(strMoviePath, i->second, strFileName);
4046 if (HasMovieInfo(strMoviePath))
4047 DeleteMovie(strMoviePath);
4048 if (HasMusicVideoInfo(strMoviePath))
4049 DeleteMusicVideo(strMoviePath);
4051 if (m_pDS2->eof() && !bMvidsChecked)
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;
4059 m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
4065 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
4071 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
4073 // if we have a multipath, set scraper for all contained paths too
4074 if(URIUtils::IsMultiPath(filePath))
4076 vector<CStdString> paths;
4077 CMultiPathDirectory::GetPaths(filePath, paths);
4079 for(unsigned i=0;i<paths.size();i++)
4080 SetScraperForPath(paths[i],scraper,settings);
4085 if (NULL == m_pDB.get()) return ;
4086 if (NULL == m_pDS.get()) return ;
4088 int idPath = AddPath(filePath);
4094 if (settings.exclude)
4095 { //NB See note in ::GetScraperForPath about strContent=='none'
4096 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
4099 { // catch clearing content, but not excluding
4100 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
4104 CStdString content = TranslateContent(scraper->Content());
4105 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);
4107 m_pDS->exec(strSQL.c_str());
4111 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4115 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
4119 if (NULL == m_pDB.get()) return false;
4120 if (NULL == m_pDS.get()) return false;
4122 CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
4123 if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
4125 bool found = m_pDS->fv(0).get_asInt() > 0;
4131 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
4139 CArtItem() { art_id = 0; media_id = 0; };
4147 bool CVideoDatabase::UpdateOldVersion(int iVersion)
4151 m_pDS->exec("ALTER TABLE settings ADD VerticalShift float");
4155 // only if MySQL is used and default character set is not utf8
4156 // string data needs to be converted to proper utf8
4157 CStdString charset = m_pDS->getDatabase()->getDefaultCharset();
4158 if (!m_sqlite && !charset.empty() && charset != "utf8")
4160 map<CStdString, CStdStringArray> tables;
4161 map<CStdString, CStdStringArray>::iterator itt;
4162 CStdStringArray::iterator itc;
4164 // columns that need to be converted
4166 CStdStringArray c_columns;
4167 for (int i = 0; i < 22; i++)
4168 c_columns.push_back(StringUtils::Format("c%02d", i));
4170 tables.insert(pair<CStdString, CStdStringArray> ("episode", c_columns));
4171 tables.insert(pair<CStdString, CStdStringArray> ("movie", c_columns));
4172 tables.insert(pair<CStdString, CStdStringArray> ("musicvideo", c_columns));
4173 tables.insert(pair<CStdString, CStdStringArray> ("tvshow", c_columns));
4177 c1.push_back("strRole");
4178 tables.insert(pair<CStdString, CStdStringArray> ("actorlinkepisode", c1));
4179 tables.insert(pair<CStdString, CStdStringArray> ("actorlinkmovie", c1));
4180 tables.insert(pair<CStdString, CStdStringArray> ("actorlinktvshow", c1));
4184 c2.push_back("strActor");
4185 tables.insert(pair<CStdString, CStdStringArray> ("actors", c2));
4188 c3.push_back("strCountry");
4189 tables.insert(pair<CStdString, CStdStringArray> ("country", c3));
4192 c4.push_back("strFilename");
4193 tables.insert(pair<CStdString, CStdStringArray> ("files", c4));
4196 c5.push_back("strGenre");
4197 tables.insert(pair<CStdString, CStdStringArray> ("genre", c5));
4200 c6.push_back("strSet");
4201 tables.insert(pair<CStdString, CStdStringArray> ("sets", c6));
4204 c7.push_back("strStudio");
4205 tables.insert(pair<CStdString, CStdStringArray> ("studio", c7));
4208 c8.push_back("strPath");
4209 tables.insert(pair<CStdString, CStdStringArray> ("path", c8));
4211 for (itt = tables.begin(); itt != tables.end(); ++itt)
4214 q = PrepareSQL("UPDATE `%s` SET", itt->first.c_str());
4215 for (itc = itt->second.begin(); itc != itt->second.end(); ++itc)
4217 q += PrepareSQL(" `%s` = CONVERT(CAST(CONVERT(`%s` USING %s) AS BINARY) USING utf8)",
4218 itc->c_str(), itc->c_str(), charset.c_str());
4219 if (*itc != itt->second.back())
4230 m_pDS->exec("ALTER TABLE movie ADD c22 text");
4231 m_pDS->exec("ALTER TABLE episode ADD c22 text");
4232 m_pDS->exec("ALTER TABLE musicvideo ADD c22 text");
4233 m_pDS->exec("ALTER TABLE tvshow ADD c22 text");
4234 // Now update our tables
4235 UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH);
4236 UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH);
4237 UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH);
4238 UpdateBasePath("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, true);
4241 { // add indices for dir entry lookups
4242 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c22(255) )");
4243 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c13(255) )");
4244 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c18(255) )");
4245 m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c16(255) )");
4249 m_pDS->exec("ALTER TABLE settings ADD ScalingMethod integer");
4250 m_pDS->exec(PrepareSQL("UPDATE settings set ScalingMethod=%i", CMediaSettings::Get().GetDefaultVideoSettings().m_ScalingMethod));
4254 // Add iOrder fields to actorlink* tables to be able to list
4255 // actors by importance
4256 m_pDS->exec("ALTER TABLE actorlinkmovie ADD iOrder integer");
4257 m_pDS->exec("ALTER TABLE actorlinktvshow ADD iOrder integer");
4258 m_pDS->exec("ALTER TABLE actorlinkepisode ADD iOrder integer");
4261 { // Add basepath link to path table for faster content retrieval, and indicies
4262 m_pDS->exec("ALTER TABLE movie ADD c23 text");
4263 m_pDS->exec("ALTER TABLE episode ADD c23 text");
4264 m_pDS->exec("ALTER TABLE musicvideo ADD c23 text");
4265 m_pDS->exec("ALTER TABLE tvshow ADD c23 text");
4266 m_pDS->dropIndex("movie", "ixMovieBasePath");
4267 m_pDS->dropIndex("musicvideo", "ixMusicVideoBasePath");
4268 m_pDS->dropIndex("episode", "ixEpisodeBasePath");
4269 m_pDS->dropIndex("tvshow", "ixTVShowBasePath");
4270 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
4271 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
4272 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
4273 m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c17(12) )");
4274 // now update the base path links
4275 UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4276 UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4277 UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4278 UpdateBasePathID("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, VIDEODB_ID_TV_PARENTPATHID);
4281 { // Change INDEX for bookmark table
4282 m_pDS->dropIndex("bookmark", "ix_bookmark");
4283 m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
4287 m_pDS->exec("ALTER TABLE settings ADD DeinterlaceMode integer");
4288 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 2 WHERE Deinterlace NOT IN (0,1)"); // anything other than none: method auto => mode force
4289 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 1 WHERE Deinterlace = 1"); // method auto => mode auto
4290 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 0, Deinterlace = 1 WHERE Deinterlace = 0"); // method none => mode off, method auto
4294 { // base paths for video_ts and bdmv files was wrong (and inconsistent depending on where and when they were scanned)
4295 CStdString where = PrepareSQL(" WHERE files.strFileName LIKE 'VIDEO_TS.IFO' or files.strFileName LIKE 'index.BDMV'");
4296 UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH, false, where);
4297 UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, false, where);
4298 UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, false, where);
4299 UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4300 UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4301 UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4305 m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4306 m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4309 { // add seasons table
4310 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4311 m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
4312 // insert all seasons for each show
4313 m_pDS->query("SELECT idShow FROM tvshow");
4314 while (!m_pDS->eof())
4316 CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4321 " WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4322 m_pDS2->exec(sql.c_str());
4323 // and the "all seasons node"
4324 sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4325 m_pDS2->exec(sql.c_str());
4331 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4332 m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
4333 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");
4334 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");
4335 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");
4336 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");
4337 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");
4338 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");
4339 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");
4341 CMediaSettings::Get().SetVideoNeedsUpdate(63);
4342 CSettings::Get().Save();
4345 { // add idShow to episode table
4346 m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4347 m_pDS->query("SELECT idEpisode FROM episode");
4348 while (!m_pDS->eof())
4350 int idEpisode = m_pDS->fv(0).get_asInt();
4351 CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4352 m_pDS2->exec(update.c_str());
4355 m_pDS->exec("DROP TABLE tvshowlinkepisode");
4356 m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
4357 m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
4361 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4362 m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
4364 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4365 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
4366 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
4367 m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
4370 { // add idSet to movie table
4371 m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4372 m_pDS->query("SELECT idMovie FROM movie");
4373 while (!m_pDS->eof())
4375 int idMovie = m_pDS->fv(0).get_asInt();
4376 CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4377 m_pDS2->exec(sql.c_str());
4380 m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4383 { // update old art URLs
4384 m_pDS->query("select art_id,url from art where url like 'image://%%'");
4385 vector< pair<int, string> > art;
4386 while (!m_pDS->eof())
4388 art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4392 for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4393 m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4396 { // update URL encoded paths
4397 m_pDS->query("select idFile, strFilename from files");
4398 vector< pair<int, string> > files;
4399 while (!m_pDS->eof())
4401 files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4406 for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4408 std::string filename = i->second;
4409 bool update = URIUtils::UpdateUrlEncoding(filename) &&
4410 (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4414 m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4418 { // Update thumb to poster or banner as applicable
4419 CTextureDatabase db;
4422 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')");
4423 vector<CArtItem> art;
4424 while (!m_pDS->eof())
4426 CTextureDetails details;
4427 if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4430 item.art_id = m_pDS->fv(0).get_asInt();
4431 item.art_url = m_pDS->fv(1).get_asString();
4432 item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4433 item.media_id = m_pDS->fv(2).get_asInt();
4434 item.media_type = m_pDS->fv(3).get_asString();
4435 if (item.art_type != "thumb")
4436 art.push_back(item);
4441 for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4443 if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4444 m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4446 m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4452 m_pDS->exec("DROP TRIGGER IF EXISTS delete_movie");
4453 m_pDS->exec("DROP TRIGGER IF EXISTS delete_tvshow");
4454 m_pDS->exec("DROP TRIGGER IF EXISTS delete_musicvideo");
4455 m_pDS->exec("DROP TRIGGER IF EXISTS delete_episode");
4456 m_pDS->exec("DROP TRIGGER IF EXISTS delete_season");
4457 m_pDS->exec("DROP TRIGGER IF EXISTS delete_set");
4458 m_pDS->exec("DROP TRIGGER IF EXISTS delete_person");
4460 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
4461 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
4462 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
4464 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
4465 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
4466 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
4468 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
4469 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
4470 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
4472 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
4473 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
4475 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
4476 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
4478 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
4479 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
4481 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
4482 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
4484 m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
4485 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
4489 { // update the runtime columns
4490 vector< pair<string, int> > tables;
4491 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4492 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4493 tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4494 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4496 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);
4497 m_pDS->query(sql.c_str());
4498 vector< pair<int, int> > videos;
4499 while (!m_pDS->eof())
4501 int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4503 videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4507 for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4508 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));
4512 { // make indices on path, file non-unique (mysql has a prefix index, and prefixes aren't necessarily unique)
4513 m_pDS->dropIndex("path", "ix_path");
4514 m_pDS->dropIndex("files", "ix_files");
4515 m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
4516 m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
4520 m_pDS->exec("ALTER TABLE settings ADD StereoMode integer");
4521 m_pDS->exec("ALTER TABLE settings ADD StereoInvert bool");
4524 m_pDS->exec("ALTER TABLE streamdetails ADD strStereoMode text");
4526 // always recreate the view after any table change
4531 int CVideoDatabase::GetMinVersion() const
4536 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4538 SScanSettings settings;
4539 bool foundDirectly = false;
4540 ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4541 if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4542 return false; // episodes
4543 return settings.parent_name_root; // shows, movies, musicvids
4546 void CVideoDatabase::UpdateBasePath(const char *table, const char *id, int column, bool shows, const CStdString &where)
4550 query = PrepareSQL("SELECT idShow,path.strPath from tvshowlinkpath join path on tvshowlinkpath.idPath=path.idPath");
4552 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);
4555 map<CStdString, bool> paths;
4556 m_pDS2->query(query.c_str());
4557 while (!m_pDS2->eof())
4559 CStdString path(m_pDS2->fv(1).get_asString());
4560 map<CStdString, bool>::iterator i = paths.find(path);
4561 if (i == paths.end())
4563 paths.insert(make_pair(path, LookupByFolders(path, shows)));
4564 i = paths.find(path);
4566 CStdString filename;
4568 ConstructPath(filename, path, m_pDS2->fv(2).get_asString());
4571 CFileItem item(filename, shows);
4572 path = item.GetBaseMoviePath(i->second);
4573 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());
4574 m_pDS->exec(sql.c_str());
4580 void CVideoDatabase::UpdateBasePathID(const char *table, const char *id, int column, int idColumn)
4582 CStdString query = PrepareSQL("SELECT %s,c%02d from %s", id, column, table);
4583 m_pDS2->query(query.c_str());
4584 while (!m_pDS2->eof())
4586 int rowID = m_pDS2->fv(0).get_asInt();
4587 CStdString path(m_pDS2->fv(1).get_asString());
4588 // find the parent path of this item
4589 int pathID = AddPath(URIUtils::GetParentPath(path));
4592 CStdString sql = PrepareSQL("UPDATE %s SET c%02d=%d WHERE %s=%d", table, idColumn, pathID, id, rowID);
4593 m_pDS->exec(sql.c_str());
4600 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4602 if(URIUtils::IsMultiPath(strPath))
4604 vector<CStdString> paths;
4605 CMultiPathDirectory::GetPaths(strPath, paths);
4608 for(unsigned i=0;i<paths.size();i++)
4609 ret |= GetPlayCounts(paths[i], items);
4614 if (URIUtils::IsPlugin(strPath))
4617 pathID = GetPathId(url.GetWithoutFilename());
4620 pathID = GetPathId(strPath);
4622 return false; // path (and thus files) aren't in the database
4627 if (NULL == m_pDB.get()) return false;
4628 if (NULL == m_pDS.get()) return false;
4630 // TODO: also test a single query for the above and below
4631 CStdString sql = PrepareSQL(
4633 " files.strFilename, files.playCount,"
4634 " bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4636 " LEFT JOIN bookmark ON"
4637 " files.idFile = bookmark.idFile AND bookmark.type = %i"
4638 " WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4640 if (RunQuery(sql) <= 0)
4643 items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4644 while (!m_pDS->eof())
4647 ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4648 CFileItemPtr item = items.Get(path);
4651 item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4652 if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4654 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4655 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4656 item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4665 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4670 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4672 int id = GetFileId(item);
4674 return 0; // not in db, so not watched
4679 if (NULL == m_pDB.get()) return -1;
4680 if (NULL == m_pDS.get()) return -1;
4682 CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4684 if (m_pDS->query(strSQL.c_str()))
4686 // there should only ever be one row returned
4687 if (m_pDS->num_rows() == 1)
4688 count = m_pDS->fv(0).get_asInt();
4695 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4700 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4702 if (NULL == m_pDB.get()) return;
4703 if (NULL == m_pDS.get()) return;
4704 if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4707 if (type == VIDEODB_CONTENT_TVSHOWS)
4708 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);
4709 else if (type == VIDEODB_CONTENT_MOVIES)
4710 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);
4714 m_pDS->exec(exec.c_str());
4716 if (type == VIDEODB_CONTENT_TVSHOWS)
4717 AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4718 else if (type == VIDEODB_CONTENT_MOVIES)
4719 AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4723 CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4727 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4730 if (item.HasProperty("original_listitem_url") &&
4731 URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4733 CFileItem item2(item);
4734 item2.SetPath(item.GetProperty("original_listitem_url").asString());
4735 id = AddFile(item2);
4742 // and mark as watched
4745 if (NULL == m_pDB.get()) return ;
4746 if (NULL == m_pDS.get()) return ;
4751 if (!date.IsValid())
4752 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4754 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4758 if (!date.IsValid())
4759 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4761 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4764 m_pDS->exec(strSQL.c_str());
4766 // We only need to announce changes to video items in the library
4767 if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4769 // Only provide the "playcount" value if it has actually changed
4770 if (item.GetVideoInfoTag()->m_playCount != count)
4773 data["playcount"] = count;
4774 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4777 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4782 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4786 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4788 SetPlayCount(item, GetPlayCount(item) + 1);
4791 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4793 SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4796 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4800 if (NULL == m_pDB.get()) return ;
4801 if (NULL == m_pDS.get()) return ;
4803 if (iType == VIDEODB_CONTENT_MOVIES)
4805 CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4808 else if (iType == VIDEODB_CONTENT_EPISODES)
4810 CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4811 content = "episode";
4813 else if (iType == VIDEODB_CONTENT_TVSHOWS)
4815 CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4818 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4820 CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4821 content = "musicvideo";
4823 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4825 CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4826 CStdString strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4827 m_pDS->exec(strSQL.c_str());
4830 if (!content.empty())
4832 SetSingleValue(iType, idMovie, FieldTitle, strNewMovieTitle);
4833 AnnounceUpdate(content, idMovie);
4838 CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4842 bool CVideoDatabase::UpdateVideoSortTitle(int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType /* = VIDEODB_CONTENT_MOVIES */)
4846 if (NULL == m_pDB.get() || NULL == m_pDS.get())
4848 if (iType != VIDEODB_CONTENT_MOVIES && iType != VIDEODB_CONTENT_TVSHOWS)
4851 CStdString content = "movie";
4852 if (iType == VIDEODB_CONTENT_TVSHOWS)
4855 if (SetSingleValue(iType, idDb, FieldSortTitle, strNewSortTitle))
4857 AnnounceUpdate(content, idDb);
4863 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());
4869 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4870 void CVideoDatabase::EraseVideoSettings()
4874 CLog::Log(LOGINFO, "Deleting settings information for all movies");
4875 m_pDS->exec("delete from settings");
4879 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4883 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4885 return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4888 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4890 return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4893 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4895 return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4898 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4902 if (NULL == m_pDB.get()) return false;
4903 if (NULL == m_pDS.get()) return false;
4906 Filter extFilter = filter;
4907 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4909 if (idContent == VIDEODB_CONTENT_MOVIES)
4911 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4912 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());
4913 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",
4914 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4916 else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4918 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4919 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4920 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",
4921 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4923 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4925 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4926 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());
4927 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",
4928 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4935 if (idContent == VIDEODB_CONTENT_MOVIES)
4937 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4938 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());
4939 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",
4940 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4941 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4943 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4945 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4946 extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4947 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4948 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4950 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4952 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4953 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());
4954 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",
4955 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4956 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4964 extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4965 extFilter.group.clear();
4966 extFilter.order.clear();
4968 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4970 CVideoDbUrl videoUrl;
4971 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4974 int iRowsFound = RunQuery(strSQL);
4975 if (iRowsFound <= 0)
4976 return iRowsFound == 0;
4980 CFileItemPtr pItem(new CFileItem());
4981 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4988 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4990 map<int, pair<CStdString,int> > mapItems;
4991 map<int, pair<CStdString,int> >::iterator it;
4992 while (!m_pDS->eof())
4994 int id = m_pDS->fv(0).get_asInt();
4995 CStdString str = m_pDS->fv(1).get_asString();
4997 // was this already found?
4998 it = mapItems.find(id);
4999 if (it == mapItems.end())
5002 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5004 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5005 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
5006 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5007 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
5014 for (it = mapItems.begin(); it != mapItems.end(); ++it)
5016 CFileItemPtr pItem(new CFileItem(it->second.first));
5017 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5018 pItem->GetVideoInfoTag()->m_type = type;
5020 CVideoDbUrl itemUrl = videoUrl;
5021 CStdString path = StringUtils::Format("%ld/", it->first);
5022 itemUrl.AppendPath(path);
5023 pItem->SetPath(itemUrl.ToString());
5025 pItem->m_bIsFolder = true;
5026 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5027 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5028 if (!items.Contains(pItem->GetPath()))
5030 pItem->SetLabelPreformated(true);
5037 while (!m_pDS->eof())
5039 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5040 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5041 pItem->GetVideoInfoTag()->m_type = type;
5043 CVideoDbUrl itemUrl = videoUrl;
5044 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5045 itemUrl.AppendPath(path);
5046 pItem->SetPath(itemUrl.ToString());
5048 pItem->m_bIsFolder = true;
5049 pItem->SetLabelPreformated(true);
5050 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5051 { // fv(3) is the number of videos watched, fv(2) is the total number. We set the playcount
5052 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5053 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
5064 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5069 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5071 CStdString mediaType;
5072 if (idContent == VIDEODB_CONTENT_MOVIES)
5073 mediaType = "movie";
5074 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5075 mediaType = "tvshow";
5076 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5077 mediaType = "musicvideo";
5083 if (NULL == m_pDB.get()) return false;
5084 if (NULL == m_pDS.get()) return false;
5086 CStdString strSQL = "SELECT %s FROM taglinks ";
5088 Filter extFilter = filter;
5089 extFilter.fields = "tag.idTag, tag.strTag";
5090 extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
5092 if (idContent == (int)VIDEODB_CONTENT_MOVIES)
5093 extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
5095 extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
5096 extFilter.AppendGroup("taglinks.idTag");
5100 extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
5101 extFilter.group.clear();
5102 extFilter.order.clear();
5104 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5106 // parse the base path to get additional filters
5107 CVideoDbUrl videoUrl;
5108 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5111 int iRowsFound = RunQuery(strSQL);
5112 if (iRowsFound <= 0)
5113 return iRowsFound == 0;
5117 CFileItemPtr pItem(new CFileItem());
5118 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5125 while (!m_pDS->eof())
5127 int idTag = m_pDS->fv(0).get_asInt();
5129 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5130 pItem->m_bIsFolder = true;
5131 pItem->GetVideoInfoTag()->m_iDbId = idTag;
5132 pItem->GetVideoInfoTag()->m_type = "tag";
5134 CVideoDbUrl itemUrl = videoUrl;
5135 CStdString path = StringUtils::Format("%ld/", idTag);
5136 itemUrl.AppendPath(path);
5137 pItem->SetPath(itemUrl.ToString());
5139 if (!items.Contains(pItem->GetPath()))
5141 pItem->SetLabelPreformated(true);
5153 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5158 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool ignoreSingleMovieSets /* = false */)
5160 if (idContent != VIDEODB_CONTENT_MOVIES)
5163 return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
5166 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
5170 if (NULL == m_pDB.get()) return false;
5171 if (NULL == m_pDS.get()) return false;
5173 CVideoDbUrl videoUrl;
5174 if (!videoUrl.FromString(strBaseDir))
5177 Filter setFilter = filter;
5178 setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
5179 if (!setFilter.order.empty())
5180 setFilter.order += ",";
5181 setFilter.order += "sets.idSet";
5183 if (!GetMoviesByWhere(strBaseDir, setFilter, items))
5187 if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
5197 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5202 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5206 if (NULL == m_pDB.get()) return false;
5207 if (NULL == m_pDS.get()) return false;
5209 CStdString strSQL = "select %s from musicvideoview ";
5210 Filter extFilter = filter;
5211 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5213 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor, path.strPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
5214 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");
5218 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
5219 extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist");
5222 extFilter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", idArtist));
5224 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
5228 extFilter.fields = "COUNT(1)";
5229 extFilter.group.clear();
5230 extFilter.order.clear();
5232 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5234 CVideoDbUrl videoUrl;
5235 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5238 int iRowsFound = RunQuery(strSQL);
5239 if (iRowsFound <= 0)
5240 return iRowsFound == 0;
5244 CFileItemPtr pItem(new CFileItem());
5245 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5252 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5254 map<int, pair<CStdString,CStdString> > mapAlbums;
5255 map<int, pair<CStdString,CStdString> >::iterator it;
5256 while (!m_pDS->eof())
5258 int lidMVideo = m_pDS->fv(1).get_asInt();
5259 CStdString strAlbum = m_pDS->fv(0).get_asString();
5260 it = mapAlbums.find(lidMVideo);
5261 // was this genre already found?
5262 if (it == mapAlbums.end())
5265 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5266 mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5272 for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5274 if (!it->second.first.empty())
5276 CFileItemPtr pItem(new CFileItem(it->second.first));
5278 CVideoDbUrl itemUrl = videoUrl;
5279 CStdString path = StringUtils::Format("%ld/", it->first);
5280 itemUrl.AppendPath(path);
5281 pItem->SetPath(itemUrl.ToString());
5283 pItem->m_bIsFolder=true;
5284 pItem->SetLabelPreformated(true);
5285 if (!items.Contains(pItem->GetPath()))
5287 pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5295 while (!m_pDS->eof())
5297 if (!m_pDS->fv(0).get_asString().empty())
5299 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5301 CVideoDbUrl itemUrl = videoUrl;
5302 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(1).get_asInt());
5303 itemUrl.AppendPath(path);
5304 pItem->SetPath(itemUrl.ToString());
5306 pItem->m_bIsFolder=true;
5307 pItem->SetLabelPreformated(true);
5308 if (!items.Contains(pItem->GetPath()))
5310 pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5319 // CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5324 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5329 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5331 return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5334 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5336 return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5339 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5341 if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5342 { // set thumbs - ideally this should be in the normal thumb setting routines
5343 for (int i = 0; i < items.Size() && !countOnly; i++)
5345 CFileItemPtr pItem = items[i];
5346 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5347 pItem->SetIconImage("DefaultArtist.png");
5349 pItem->SetIconImage("DefaultActor.png");
5356 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5358 if (NULL == m_pDB.get()) return false;
5359 if (NULL == m_pDS.get()) return false;
5363 // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5364 // point. This means that we *MUST* filter these levels as you'll get double ups. Ideally we'd allow playcount
5365 // to filter through as we normally do for tvshows to save this happening.
5366 // Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5367 // Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5370 // General routine that the other actor/director/writer routines call
5372 // get primary genres for movies
5374 Filter extFilter = filter;
5375 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5377 if (idContent == VIDEODB_CONTENT_MOVIES)
5379 strSQL = "select %s from actors ";
5380 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5381 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",
5382 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5384 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5386 strSQL = "select %s from actors ";
5387 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5388 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",
5389 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5391 else if (idContent == VIDEODB_CONTENT_EPISODES)
5393 strSQL = "select %s from actors ";
5394 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5395 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",
5396 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5398 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5400 strSQL = "select %s from actors ";
5401 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5402 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",
5403 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5410 if (idContent == VIDEODB_CONTENT_MOVIES)
5412 strSQL ="select %s from actors ";
5413 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5414 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",
5415 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5416 extFilter.AppendGroup("actors.idActor");
5418 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5420 strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5421 extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5422 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5423 type.c_str(), type.c_str(), type.c_str()));
5425 else if (idContent == VIDEODB_CONTENT_EPISODES)
5427 strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5428 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5429 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5430 type.c_str(), type.c_str(), type.c_str()));
5431 extFilter.AppendGroup("actors.idActor");
5433 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5435 strSQL = "select %s from actors ";
5436 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5437 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",
5438 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5439 extFilter.AppendGroup("actors.idActor");
5447 extFilter.fields = "COUNT(1)";
5448 extFilter.group.clear();
5449 extFilter.order.clear();
5451 strSQL = StringUtils::Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5453 CVideoDbUrl videoUrl;
5454 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5458 unsigned int time = XbmcThreads::SystemClockMillis();
5459 if (!m_pDS->query(strSQL.c_str())) return false;
5460 CLog::Log(LOGDEBUG, "%s - query took %i ms",
5461 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5462 int iRowsFound = m_pDS->num_rows();
5463 if (iRowsFound == 0)
5471 CFileItemPtr pItem(new CFileItem());
5472 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5479 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5481 map<int, CActor> mapActors;
5482 map<int, CActor>::iterator it;
5484 while (!m_pDS->eof())
5486 int idActor = m_pDS->fv(0).get_asInt();
5488 actor.name = m_pDS->fv(1).get_asString();
5489 actor.thumb = m_pDS->fv(2).get_asString();
5490 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5491 actor.playcount = m_pDS->fv(3).get_asInt();
5492 it = mapActors.find(idActor);
5493 // is this actor already known?
5494 if (it == mapActors.end())
5497 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5498 mapActors.insert(pair<int, CActor>(idActor, actor));
5504 for (it=mapActors.begin();it != mapActors.end();++it)
5506 CFileItemPtr pItem(new CFileItem(it->second.name));
5508 CVideoDbUrl itemUrl = videoUrl;
5509 CStdString path = StringUtils::Format("%ld/", it->first);
5510 itemUrl.AppendPath(path);
5511 pItem->SetPath(itemUrl.ToString());
5513 pItem->m_bIsFolder=true;
5514 pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5515 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5516 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5517 pItem->GetVideoInfoTag()->m_type = type;
5523 while (!m_pDS->eof())
5527 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5529 CVideoDbUrl itemUrl = videoUrl;
5530 CStdString path = StringUtils::Format("%ld/", m_pDS->fv(0).get_asInt());
5531 itemUrl.AppendPath(path);
5532 pItem->SetPath(itemUrl.ToString());
5534 pItem->m_bIsFolder=true;
5535 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5536 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5537 pItem->GetVideoInfoTag()->m_type = type;
5538 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5540 // fv(4) is the number of videos watched, fv(3) is the total number. We set the playcount
5541 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5542 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5544 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5545 pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5552 CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5553 return items.Size() > 0;
5558 CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5559 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5566 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5571 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5575 if (NULL == m_pDB.get()) return false;
5576 if (NULL == m_pDS.get()) return false;
5579 Filter extFilter = filter;
5580 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5582 if (idContent == VIDEODB_CONTENT_MOVIES)
5584 strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5585 extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5587 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5589 strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5590 extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5592 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5594 strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5595 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5603 if (idContent == VIDEODB_CONTENT_MOVIES)
5605 strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5606 extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5607 extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5609 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5610 strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5611 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5613 strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5614 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5615 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5621 CVideoDbUrl videoUrl;
5622 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5625 int iRowsFound = RunQuery(strSQL);
5626 if (iRowsFound <= 0)
5627 return iRowsFound == 0;
5629 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5631 map<int, pair<CStdString,int> > mapYears;
5632 map<int, pair<CStdString,int> >::iterator it;
5633 while (!m_pDS->eof())
5636 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5639 time.SetFromDateString(m_pDS->fv(0).get_asString());
5640 lYear = time.GetYear();
5642 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5643 lYear = m_pDS->fv(0).get_asInt();
5644 it = mapYears.find(lYear);
5645 if (it == mapYears.end())
5648 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5650 CStdString year = StringUtils::Format("%d", lYear);
5651 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5652 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5654 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5661 for (it=mapYears.begin();it != mapYears.end();++it)
5665 CFileItemPtr pItem(new CFileItem(it->second.first));
5667 CVideoDbUrl itemUrl = videoUrl;
5668 CStdString path = StringUtils::Format("%ld/", it->first);
5669 itemUrl.AppendPath(path);
5670 pItem->SetPath(itemUrl.ToString());
5672 pItem->m_bIsFolder=true;
5673 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5674 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5680 while (!m_pDS->eof())
5683 CStdString strLabel;
5684 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5687 time.SetFromDateString(m_pDS->fv(0).get_asString());
5688 lYear = time.GetYear();
5689 strLabel = StringUtils::Format("%i",lYear);
5691 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5693 lYear = m_pDS->fv(0).get_asInt();
5694 strLabel = m_pDS->fv(0).get_asString();
5701 CFileItemPtr pItem(new CFileItem(strLabel));
5703 CVideoDbUrl itemUrl = videoUrl;
5704 CStdString path = StringUtils::Format("%ld/", lYear);
5705 itemUrl.AppendPath(path);
5706 pItem->SetPath(itemUrl.ToString());
5708 pItem->m_bIsFolder=true;
5709 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5711 // fv(2) is the number of videos watched, fv(1) is the total number. We set the playcount
5712 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5713 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5716 // take care of dupes ..
5717 if (!items.Contains(pItem->GetPath()))
5729 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5734 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5738 if (NULL == m_pDB.get()) return false;
5739 if (NULL == m_pDS.get()) return false;
5741 // look for duplicate show titles and stack them into a list
5744 CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5745 CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5746 if (!m_pDS->query(strSQL.c_str())) return false;
5747 int iRows = m_pDS->num_rows();
5748 if (iRows == 0) return false; // this should never happen!
5750 { // more than one show, so stack them up
5752 while (!m_pDS->eof())
5754 strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5757 strIn[strIn.GetLength() - 1] = ')'; // replace last , with )
5764 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5769 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5773 if (NULL == m_pDB.get()) return false;
5774 if (NULL == m_pDS.get()) return false;
5776 // parse the base path to get additional filters
5777 CVideoDbUrl videoUrl;
5778 if (!videoUrl.FromString(strBaseDir))
5781 CStdString strIn = PrepareSQL("= %i", idShow);
5782 GetStackedTvShowList(idShow, strIn);
5784 CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5786 "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5787 "seasons.idSeason, "
5788 "count(1), count(files.playCount) "
5789 "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);
5792 filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5793 "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5794 "JOIN files ON files.idFile = episodeview.idFile "
5795 "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5796 "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5797 filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5798 filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5800 videoUrl.AddOption("tvshowid", idShow);
5803 videoUrl.AddOption("actorid", idActor);
5804 else if (idDirector != -1)
5805 videoUrl.AddOption("directorid", idDirector);
5806 else if (idGenre != -1)
5807 videoUrl.AddOption("genreid", idGenre);
5808 else if (idYear != -1)
5809 videoUrl.AddOption("year", idYear);
5811 if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5814 int iRowsFound = RunQuery(strSQL);
5815 if (iRowsFound <= 0)
5816 return iRowsFound == 0;
5818 // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5819 CStdString showTitle = m_pDS->fv(2).get_asString();
5820 CStdString showPlot = m_pDS->fv(3).get_asString();
5821 CStdString showPremiered = m_pDS->fv(4).get_asString();
5822 CStdString showStudio = m_pDS->fv(6).get_asString();
5823 CStdString showMPAARating = m_pDS->fv(7).get_asString();
5825 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5827 map<int, CSeason> mapSeasons;
5828 map<int, CSeason>::iterator it;
5829 while (!m_pDS->eof())
5831 int iSeason = m_pDS->fv(0).get_asInt();
5832 it = mapSeasons.find(iSeason);
5834 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5839 if (it == mapSeasons.end())
5842 season.path = m_pDS->fv(1).get_asString();
5843 season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5844 season.id = m_pDS->fv(8).get_asInt();
5845 season.numEpisodes = m_pDS->fv(9).get_asInt();
5846 season.numWatched = m_pDS->fv(10).get_asInt();
5847 mapSeasons.insert(make_pair(iSeason, season));
5853 for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5855 int iSeason = it->first;
5856 CStdString strLabel;
5858 strLabel = g_localizeStrings.Get(20381);
5860 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5861 CFileItemPtr pItem(new CFileItem(strLabel));
5863 CVideoDbUrl itemUrl = videoUrl;
5864 CStdString strDir = StringUtils::Format("%ld/", it->first);
5865 itemUrl.AppendPath(strDir);
5866 pItem->SetPath(itemUrl.ToString());
5868 pItem->m_bIsFolder=true;
5869 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5870 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5871 pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5872 pItem->GetVideoInfoTag()->m_type = "season";
5873 pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5874 pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5875 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5876 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5877 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5878 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5879 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5880 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5881 pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5882 pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5883 pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5884 pItem->SetProperty("watchedepisodes", it->second.numWatched);
5885 pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5886 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5887 pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5888 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5894 while (!m_pDS->eof())
5896 int iSeason = m_pDS->fv(0).get_asInt();
5897 CStdString strLabel;
5899 strLabel = g_localizeStrings.Get(20381);
5901 strLabel = StringUtils::Format(g_localizeStrings.Get(20358), iSeason);
5902 CFileItemPtr pItem(new CFileItem(strLabel));
5904 CVideoDbUrl itemUrl = videoUrl;
5905 CStdString strDir = StringUtils::Format("%ld/", iSeason);
5906 itemUrl.AppendPath(strDir);
5907 pItem->SetPath(itemUrl.ToString());
5909 pItem->m_bIsFolder=true;
5910 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5911 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5912 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5913 pItem->GetVideoInfoTag()->m_type = "season";
5914 pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5915 pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5916 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5917 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5918 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5919 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5920 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5921 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5922 int totalEpisodes = m_pDS->fv(9).get_asInt();
5923 int watchedEpisodes = m_pDS->fv(10).get_asInt();
5924 pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5925 pItem->SetProperty("totalepisodes", totalEpisodes);
5926 pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5927 pItem->SetProperty("watchedepisodes", watchedEpisodes);
5928 pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5929 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5930 pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5931 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5938 // now add any linked movies
5939 if (getLinkedMovies)
5942 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5943 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5944 CFileItemList movieItems;
5945 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
5947 if (movieItems.Size() > 0)
5948 items.Append(movieItems);
5955 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5960 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5962 if (NULL == m_pDB.get() || NULL == m_pDS.get())
5965 if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5968 SortDescription sorting = sortDescription;
5969 if (sortDescription.sortBy == SortByFile ||
5970 sortDescription.sortBy == SortByTitle ||
5971 sortDescription.sortBy == SortBySortTitle ||
5972 sortDescription.sortBy == SortByLabel ||
5973 sortDescription.sortBy == SortByDateAdded ||
5974 sortDescription.sortBy == SortByRating ||
5975 sortDescription.sortBy == SortByYear ||
5976 sortDescription.sortBy == SortByLastPlayed ||
5977 sortDescription.sortBy == SortByPlaycount)
5978 sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5980 bool success = false;
5983 case MediaTypeMovie:
5984 success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5987 case MediaTypeTvShow:
5988 success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
5991 case MediaTypeEpisode:
5992 success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
5995 case MediaTypeMusicVideo:
5996 success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
6003 items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
6007 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6009 CVideoDbUrl videoUrl;
6010 if (!videoUrl.FromString(strBaseDir))
6013 return GetItems(strBaseDir, videoUrl.GetType(), videoUrl.GetItemType(), items, filter, sortDescription);
6016 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, const CStdString &mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6018 VIDEODB_CONTENT_TYPE contentType;
6019 if (mediaType.Equals("movies"))
6020 contentType = VIDEODB_CONTENT_MOVIES;
6021 else if (mediaType.Equals("tvshows"))
6023 if (itemType.Equals("episodes"))
6024 contentType = VIDEODB_CONTENT_EPISODES;
6026 contentType = VIDEODB_CONTENT_TVSHOWS;
6028 else if (mediaType.Equals("musicvideos"))
6029 contentType = VIDEODB_CONTENT_MUSICVIDEOS;
6033 return GetItems(strBaseDir, contentType, itemType, items, filter, sortDescription);
6036 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, VIDEODB_CONTENT_TYPE mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
6038 if (itemType.Equals("movies") && (mediaType == VIDEODB_CONTENT_MOVIES || mediaType == VIDEODB_CONTENT_MOVIE_SETS))
6039 return GetMoviesByWhere(strBaseDir, filter, items, sortDescription);
6040 else if (itemType.Equals("tvshows") && mediaType == VIDEODB_CONTENT_TVSHOWS)
6041 return GetTvShowsByWhere(strBaseDir, filter, items, sortDescription);
6042 else if (itemType.Equals("musicvideos") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6043 return GetMusicVideosByWhere(strBaseDir, filter, items, true, sortDescription);
6044 else if (itemType.Equals("episodes") && mediaType == VIDEODB_CONTENT_EPISODES)
6045 return GetEpisodesByWhere(strBaseDir, filter, items, true, sortDescription);
6046 else if (itemType.Equals("seasons") && mediaType == VIDEODB_CONTENT_TVSHOWS)
6047 return GetSeasonsNav(strBaseDir, items);
6048 else if (itemType.Equals("genres"))
6049 return GetGenresNav(strBaseDir, items, mediaType, filter);
6050 else if (itemType.Equals("years"))
6051 return GetYearsNav(strBaseDir, items, mediaType, filter);
6052 else if (itemType.Equals("actors"))
6053 return GetActorsNav(strBaseDir, items, mediaType, filter);
6054 else if (itemType.Equals("directors"))
6055 return GetDirectorsNav(strBaseDir, items, mediaType, filter);
6056 else if (itemType.Equals("writers"))
6057 return GetWritersNav(strBaseDir, items, mediaType, filter);
6058 else if (itemType.Equals("studios"))
6059 return GetStudiosNav(strBaseDir, items, mediaType, filter);
6060 else if (itemType.Equals("sets"))
6061 return GetSetsNav(strBaseDir, items, mediaType, filter);
6062 else if (itemType.Equals("countries"))
6063 return GetCountriesNav(strBaseDir, items, mediaType, filter);
6064 else if (itemType.Equals("tags"))
6065 return GetTagsNav(strBaseDir, items, mediaType, filter);
6066 else if (itemType.Equals("artists") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6067 return GetActorsNav(strBaseDir, items, mediaType, filter);
6068 else if (itemType.Equals("albums") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
6069 return GetMusicVideoAlbumsNav(strBaseDir, items, -1, filter);
6074 CStdString CVideoDatabase::GetItemById(const CStdString &itemType, int id)
6076 if (itemType.Equals("genres"))
6077 return GetGenreById(id);
6078 else if (itemType.Equals("years"))
6079 return StringUtils::Format("%d", id);
6080 else if (itemType.Equals("actors") || itemType.Equals("directors") || itemType.Equals("artists"))
6081 return GetPersonById(id);
6082 else if (itemType.Equals("studios"))
6083 return GetStudioById(id);
6084 else if (itemType.Equals("sets"))
6085 return GetSetById(id);
6086 else if (itemType.Equals("countries"))
6087 return GetCountryById(id);
6088 else if (itemType.Equals("tags"))
6089 return GetTagById(id);
6090 else if (itemType.Equals("albums"))
6091 return GetMusicVideoAlbumById(id);
6096 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
6097 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
6098 int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
6099 const SortDescription &sortDescription /* = SortDescription() */)
6101 CVideoDbUrl videoUrl;
6102 if (!videoUrl.FromString(strBaseDir))
6106 videoUrl.AddOption("genreid", idGenre);
6107 else if (idCountry > 0)
6108 videoUrl.AddOption("countryid", idCountry);
6109 else if (idStudio > 0)
6110 videoUrl.AddOption("studioid", idStudio);
6111 else if (idDirector > 0)
6112 videoUrl.AddOption("directorid", idDirector);
6113 else if (idYear > 0)
6114 videoUrl.AddOption("year", idYear);
6115 else if (idActor > 0)
6116 videoUrl.AddOption("actorid", idActor);
6118 videoUrl.AddOption("setid", idSet);
6120 videoUrl.AddOption("tagid", idTag);
6123 return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
6126 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
6133 if (NULL == m_pDB.get()) return false;
6134 if (NULL == m_pDS.get()) return false;
6136 // parse the base path to get additional filters
6137 CVideoDbUrl videoUrl;
6138 Filter extFilter = filter;
6139 SortDescription sorting = sortDescription;
6140 if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
6145 CStdString strSQL = "select %s from movieview ";
6146 CStdString strSQLExtra;
6147 if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
6150 // Apply the limiting directly here if there's no special sorting but limiting
6151 if (extFilter.limit.empty() &&
6152 sorting.sortBy == SortByNone &&
6153 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6155 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6156 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6159 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6161 int iRowsFound = RunQuery(strSQL);
6162 if (iRowsFound <= 0)
6163 return iRowsFound == 0;
6165 // store the total value of items as a property
6166 if (total < iRowsFound)
6168 items.SetProperty("total", total);
6170 DatabaseResults results;
6171 results.reserve(iRowsFound);
6173 if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
6176 // get data from returned rows
6177 items.Reserve(results.size());
6178 const query_data &data = m_pDS->get_result_set().records;
6179 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6181 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6182 const dbiplus::sql_record* const record = data.at(targetRow);
6184 CVideoInfoTag movie = GetDetailsForMovie(record);
6185 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6186 g_passwordManager.bMasterUser ||
6187 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6189 CFileItemPtr pItem(new CFileItem(movie));
6191 CVideoDbUrl itemUrl = videoUrl;
6192 CStdString path = StringUtils::Format("%ld", movie.m_iDbId);
6193 itemUrl.AppendPath(path);
6194 pItem->SetPath(itemUrl.ToString());
6196 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
6207 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6212 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
6213 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
6214 const SortDescription &sortDescription /* = SortDescription() */)
6216 CVideoDbUrl videoUrl;
6217 if (!videoUrl.FromString(strBaseDir))
6221 videoUrl.AddOption("genreid", idGenre);
6222 else if (idStudio != -1)
6223 videoUrl.AddOption("studioid", idStudio);
6224 else if (idDirector != -1)
6225 videoUrl.AddOption("directorid", idDirector);
6226 else if (idYear != -1)
6227 videoUrl.AddOption("year", idYear);
6228 else if (idActor != -1)
6229 videoUrl.AddOption("actorid", idActor);
6230 else if (idTag != -1)
6231 videoUrl.AddOption("tagid", idTag);
6234 return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
6237 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
6243 if (NULL == m_pDB.get()) return false;
6244 if (NULL == m_pDS.get()) return false;
6248 CStdString strSQL = "SELECT %s FROM tvshowview ";
6249 CVideoDbUrl videoUrl;
6250 CStdString strSQLExtra;
6251 Filter extFilter = filter;
6252 SortDescription sorting = sortDescription;
6253 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6256 // Apply the limiting directly here if there's no special sorting but limiting
6257 if (extFilter.limit.empty() &&
6258 sorting.sortBy == SortByNone &&
6259 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6261 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6262 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6265 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6267 int iRowsFound = RunQuery(strSQL);
6268 if (iRowsFound <= 0)
6269 return iRowsFound == 0;
6271 // store the total value of items as a property
6272 if (total < iRowsFound)
6274 items.SetProperty("total", total);
6276 DatabaseResults results;
6277 results.reserve(iRowsFound);
6278 if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
6281 // get data from returned rows
6282 items.Reserve(results.size());
6283 const query_data &data = m_pDS->get_result_set().records;
6284 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6286 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6287 const dbiplus::sql_record* const record = data.at(targetRow);
6289 CVideoInfoTag movie = GetDetailsForTvShow(record, false);
6290 if ((CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6291 g_passwordManager.bMasterUser ||
6292 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video"))) &&
6293 (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
6295 CFileItemPtr pItem(new CFileItem(movie));
6297 CVideoDbUrl itemUrl = videoUrl;
6298 CStdString path = StringUtils::Format("%ld/", record->at(0).get_asInt());
6299 itemUrl.AppendPath(path);
6300 pItem->SetPath(itemUrl.ToString());
6302 pItem->m_dateTime = movie.m_premiered;
6303 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6304 pItem->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
6305 pItem->SetProperty("totalepisodes", movie.m_iEpisode);
6306 pItem->SetProperty("numepisodes", movie.m_iEpisode); // will be changed later to reflect watchmode setting
6307 pItem->SetProperty("watchedepisodes", movie.m_playCount);
6308 pItem->SetProperty("unwatchedepisodes", movie.m_iEpisode - movie.m_playCount);
6309 pItem->GetVideoInfoTag()->m_playCount = (movie.m_iEpisode == movie.m_playCount) ? 1 : 0;
6310 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6315 Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6323 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6328 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6330 if (maintainSortOrder)
6332 // save current sort order
6333 for (int i = 0; i < items.Size(); i++)
6334 items[i]->m_iprogramCount = i;
6339 case VIDEODB_CONTENT_TVSHOWS:
6342 items.Sort(SortBySortTitle, SortOrderAscending);
6345 while (i < items.Size())
6347 CFileItemPtr pItem = items.Get(i);
6348 CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6349 CStdString strFanArt = pItem->GetArt("fanart");
6352 bool bStacked = false;
6353 while (j < items.Size())
6355 CFileItemPtr jItem = items.Get(j);
6357 // matching title? append information
6358 if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6360 if (jItem->GetVideoInfoTag()->m_premiered !=
6361 pItem->GetVideoInfoTag()->m_premiered)
6368 // increment episode counts
6369 pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6370 pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6371 pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6372 pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6373 pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6375 // adjust lastplayed
6376 if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6377 pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6379 // check for fanart if not already set
6380 if (strFanArt.empty())
6381 strFanArt = jItem->GetArt("fanart");
6383 // remove duplicate entry
6386 // no match? exit loop
6390 // update playcount and fanart
6393 pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6394 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6395 if (!strFanArt.empty())
6396 pItem->SetArt("fanart", strFanArt);
6398 // increment i to j which is the next item
6403 // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6404 // so that if we eventually want to stack during scan we can utilize it.
6406 case VIDEODB_CONTENT_EPISODES:
6408 // sort by ShowTitle, Episode, Filename
6409 items.Sort(SortByEpisodeNumber, SortOrderAscending);
6412 while (i < items.Size())
6414 CFileItemPtr pItem = items.Get(i);
6415 CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6416 int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6417 int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6418 //CStdString strFanArt = pItem->GetArt("fanart");
6420 // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6421 CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6422 bool bDvdFolder = StringUtils::EndsWithNoCase(strFileNameAndPath, "video_ts.ifo");
6424 vector<CStdString> paths;
6425 paths.push_back(strFileNameAndPath);
6426 CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6429 int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6430 while (j < items.Size())
6432 CFileItemPtr jItem = items.Get(j);
6433 const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6434 CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6436 CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6437 // compare path, season, episode
6440 jTag->m_strPath.Equals(strPath) &&
6441 jTag->m_iSeason == iSeason &&
6442 jTag->m_iEpisode == iEpisode
6445 // keep checking to see if this is dvd folder
6448 bDvdFolder = StringUtils::EndsWithNoCase(jFileNameAndPath, "video_ts.ifo");
6449 // if we have a dvd folder, we stack differently
6452 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6454 paths.push_back(jFileNameAndPath);
6458 // increment playcount
6459 iPlayCount += jTag->m_playCount;
6461 // episodes dont have fanart yet
6462 //if (strFanArt.empty())
6463 // strFanArt = jItem->GetArt("fanart");
6465 paths.push_back(jFileNameAndPath);
6469 // remove duplicate entry
6473 // no match? exit loop
6477 // update playcount and fanart if we have a stacked entry
6478 if (paths.size() > 1)
6480 CStackDirectory dir;
6481 CStdString strStack;
6482 dir.ConstructStackPath(paths, strStack);
6483 pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6484 pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6485 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6487 // episodes dont have fanart yet
6488 //if (!strFanArt.empty())
6489 // pItem->SetArt("fanart", strFanArt);
6491 // increment i to j which is the next item
6497 // stack other types later
6501 if (maintainSortOrder)
6503 // restore original sort order - essential for smartplaylists
6504 items.Sort(SortByProgramCount, SortOrderAscending);
6508 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6510 CVideoDbUrl videoUrl;
6511 if (!videoUrl.FromString(strBaseDir))
6517 strIn = PrepareSQL("= %i", idShow);
6518 GetStackedTvShowList(idShow, strIn);
6520 videoUrl.AddOption("tvshowid", idShow);
6522 videoUrl.AddOption("season", idSeason);
6525 videoUrl.AddOption("genreid", idGenre);
6526 else if (idYear !=-1)
6527 videoUrl.AddOption("year", idYear);
6528 else if (idActor != -1)
6529 videoUrl.AddOption("actorid", idActor);
6531 else if (idYear != -1)
6532 videoUrl.AddOption("year", idYear);
6534 if (idDirector != -1)
6535 videoUrl.AddOption("directorid", idDirector);
6538 bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6540 if (idSeason == -1 && idShow != -1)
6541 { // add any linked movies
6543 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6544 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6545 CFileItemList movieItems;
6546 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
6548 if (movieItems.Size() > 0)
6549 items.Append(movieItems);
6555 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6562 if (NULL == m_pDB.get()) return false;
6563 if (NULL == m_pDS.get()) return false;
6567 CStdString strSQL = "select %s from episodeview ";
6568 CVideoDbUrl videoUrl;
6569 CStdString strSQLExtra;
6570 Filter extFilter = filter;
6571 SortDescription sorting = sortDescription;
6572 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6575 // Apply the limiting directly here if there's no special sorting but limiting
6576 if (extFilter.limit.empty() &&
6577 sorting.sortBy == SortByNone &&
6578 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6580 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6581 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6584 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6586 int iRowsFound = RunQuery(strSQL);
6587 if (iRowsFound <= 0)
6588 return iRowsFound == 0;
6590 // store the total value of items as a property
6591 if (total < iRowsFound)
6593 items.SetProperty("total", total);
6595 DatabaseResults results;
6596 results.reserve(iRowsFound);
6597 if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6600 // get data from returned rows
6601 items.Reserve(results.size());
6602 CLabelFormatter formatter("%H. %T", "");
6604 const query_data &data = m_pDS->get_result_set().records;
6605 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6607 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6608 const dbiplus::sql_record* const record = data.at(targetRow);
6610 CVideoInfoTag movie = GetDetailsForEpisode(record);
6611 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6612 g_passwordManager.bMasterUser ||
6613 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6615 CFileItemPtr pItem(new CFileItem(movie));
6616 formatter.FormatLabel(pItem.get());
6618 int idEpisode = record->at(0).get_asInt();
6620 CVideoDbUrl itemUrl = videoUrl;
6622 if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6623 path = StringUtils::Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6625 path = StringUtils::Format("%ld", idEpisode);
6626 itemUrl.AppendPath(path);
6627 pItem->SetPath(itemUrl.ToString());
6629 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6630 pItem->m_dateTime = movie.m_firstAired;
6631 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6642 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6647 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() */)
6649 CVideoDbUrl videoUrl;
6650 if (!videoUrl.FromString(strBaseDir))
6654 videoUrl.AddOption("genreid", idGenre);
6655 else if (idStudio != -1)
6656 videoUrl.AddOption("studioid", idStudio);
6657 else if (idDirector != -1)
6658 videoUrl.AddOption("directorid", idDirector);
6659 else if (idYear !=-1)
6660 videoUrl.AddOption("year", idYear);
6661 else if (idArtist != -1)
6662 videoUrl.AddOption("artistid", idArtist);
6663 else if (idTag != -1)
6664 videoUrl.AddOption("tagid", idTag);
6666 videoUrl.AddOption("albumid", idAlbum);
6669 return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6672 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6675 filter.order = "dateAdded desc, idMovie desc";
6676 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6677 return GetMoviesByWhere(strBaseDir, filter, items);
6680 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6683 filter.order = "dateAdded desc, idEpisode desc";
6684 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6685 return GetEpisodesByWhere(strBaseDir, filter, items, false);
6688 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6691 filter.order = "dateAdded desc, idMVideo desc";
6692 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6693 return GetMusicVideosByWhere(strBaseDir, filter, items);
6696 CStdString CVideoDatabase::GetGenreById(int id)
6698 return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6701 CStdString CVideoDatabase::GetCountryById(int id)
6703 return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6706 CStdString CVideoDatabase::GetSetById(int id)
6708 return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6711 CStdString CVideoDatabase::GetTagById(int id)
6713 return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6716 CStdString CVideoDatabase::GetPersonById(int id)
6718 return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6721 CStdString CVideoDatabase::GetStudioById(int id)
6723 return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6726 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6728 return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6731 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6733 return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6736 bool CVideoDatabase::HasSets() const
6740 if (NULL == m_pDB.get()) return false;
6741 if (NULL == m_pDS.get()) return false;
6743 m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6744 "JOIN sets ON sets.idSet = movieview.idSet "
6745 "GROUP BY movieview.idSet HAVING c>1");
6747 bool bResult = (m_pDS->num_rows() > 0);
6753 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6758 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6762 if (NULL == m_pDB.get()) return false;
6763 if (NULL == m_pDS2.get()) return false;
6765 // make sure we use m_pDS2, as this is called in loops using m_pDS
6766 CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6767 m_pDS2->query( strSQL.c_str() );
6771 result=m_pDS2->fv(0).get_asInt();
6778 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6783 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6786 sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6787 CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6790 return atoi(id.c_str());
6793 bool CVideoDatabase::HasContent()
6795 return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6796 HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6797 HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6800 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6802 bool result = false;
6805 if (NULL == m_pDB.get()) return false;
6806 if (NULL == m_pDS.get()) return false;
6809 if (type == VIDEODB_CONTENT_MOVIES)
6810 sql = "select count(1) from movie";
6811 else if (type == VIDEODB_CONTENT_TVSHOWS)
6812 sql = "select count(1) from tvshow";
6813 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6814 sql = "select count(1) from musicvideo";
6815 m_pDS->query( sql.c_str() );
6818 result = (m_pDS->fv(0).get_asInt() > 0);
6824 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6829 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6833 if (NULL == m_pDB.get()) return 0;
6834 if (NULL == m_pDS.get()) return 0;
6836 CStdString strSQL = StringUtils::Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6837 m_pDS->query( strSQL.c_str() );
6841 iResult = m_pDS->fv("nummovies").get_asInt();
6848 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6853 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6855 SScanSettings settings;
6856 return GetScraperForPath(strPath, settings);
6859 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6862 return GetScraperForPath(strPath, settings, dummy);
6865 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6867 foundDirectly = false;
6870 if (strPath.empty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6873 CStdString strPath2;
6875 if (URIUtils::IsMultiPath(strPath))
6876 strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6880 CStdString strPath1 = URIUtils::GetDirectory(strPath2);
6881 int idPath = GetPathId(strPath1);
6885 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);
6886 m_pDS->query( strSQL.c_str() );
6890 CONTENT_TYPE content = CONTENT_NONE;
6892 { // path is stored in db
6894 if (m_pDS->fv("path.exclude").get_asBool())
6896 settings.exclude = true;
6898 return ScraperPtr();
6900 settings.exclude = false;
6902 // try and ascertain scraper for this path
6903 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6904 strcontent.ToLower();
6905 content = TranslateContent(strcontent);
6907 //FIXME paths stored should not have empty strContent
6908 //assert(content != CONTENT_NONE);
6909 CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6912 if (!scraperID.empty() &&
6913 CAddonMgr::Get().GetAddon(scraperID, addon))
6915 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6917 return ScraperPtr();
6919 // store this path's content & settings
6920 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6921 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6922 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6923 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6927 if (content == CONTENT_NONE)
6928 { // this path is not saved in db
6929 // we must drill up until a scraper is configured
6930 CStdString strParent;
6931 while (URIUtils::GetParentPath(strPath1, strParent))
6935 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());
6936 m_pDS->query(strSQL.c_str());
6938 CONTENT_TYPE content = CONTENT_NONE;
6942 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6943 strcontent.ToLower();
6944 if (m_pDS->fv("path.exclude").get_asBool())
6946 settings.exclude = true;
6952 content = TranslateContent(strcontent);
6955 if (content != CONTENT_NONE &&
6956 CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6958 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6959 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6960 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6961 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6962 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6963 settings.exclude = false;
6967 strPath1 = strParent;
6972 if (!scraper || scraper->Content() == CONTENT_NONE)
6973 return ScraperPtr();
6975 if (scraper->Content() == CONTENT_TVSHOWS)
6977 settings.recurse = 0;
6978 if(settings.parent_name) // single show
6980 settings.parent_name_root = settings.parent_name = (iFound == 1);
6984 settings.parent_name_root = settings.parent_name = (iFound == 2);
6987 else if (scraper->Content() == CONTENT_MOVIES)
6989 settings.recurse = settings.recurse - (iFound-1);
6990 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6992 else if (scraper->Content() == CONTENT_MUSICVIDEOS)
6994 settings.recurse = settings.recurse - (iFound-1);
6995 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
7000 return ScraperPtr();
7002 foundDirectly = (iFound == 1);
7007 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7009 return ScraperPtr();
7012 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
7014 SScanSettings settings;
7015 bool foundDirectly = false;
7016 ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
7019 if (scraper->Content() == CONTENT_TVSHOWS)
7020 { // check for episodes or seasons. Assumptions are:
7021 // 1. if episodes are in the path then we're in episodes.
7022 // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
7023 // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
7024 CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
7025 m_pDS->query( sql.c_str() );
7026 if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
7028 return foundDirectly ? "tvshows" : "seasons";
7030 return TranslateContent(scraper->Content());
7035 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
7041 if (NULL == m_pDB.get()) return;
7042 if (NULL == m_pDS.get()) return;
7044 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7045 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());
7047 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
7048 m_pDS->query( strSQL.c_str() );
7050 while (!m_pDS->eof())
7052 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7053 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
7054 *CMediaSourceSettings::Get().GetSources("video")))
7060 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7061 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7062 pItem->SetPath("videodb://movies/genres/"+ strDir);
7063 pItem->m_bIsFolder=true;
7071 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7075 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
7081 if (NULL == m_pDB.get()) return;
7082 if (NULL == m_pDS.get()) return;
7084 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7085 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());
7087 strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
7088 m_pDS->query( strSQL.c_str() );
7090 while (!m_pDS->eof())
7092 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7093 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
7094 *CMediaSourceSettings::Get().GetSources("video")))
7100 CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
7101 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
7102 pItem->SetPath("videodb://movies/genres/"+ strDir);
7103 pItem->m_bIsFolder=true;
7111 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7115 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
7121 if (NULL == m_pDB.get()) return;
7122 if (NULL == m_pDS.get()) return;
7124 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7125 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());
7127 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
7128 m_pDS->query( strSQL.c_str() );
7130 while (!m_pDS->eof())
7132 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7133 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7139 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7140 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7141 pItem->SetPath("videodb://tvshows/genres/"+ strDir);
7142 pItem->m_bIsFolder=true;
7150 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7154 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
7160 if (NULL == m_pDB.get()) return;
7161 if (NULL == m_pDS.get()) return;
7163 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7164 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());
7166 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());
7167 m_pDS->query( strSQL.c_str() );
7169 while (!m_pDS->eof())
7171 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7172 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7178 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7179 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7180 pItem->SetPath("videodb://movies/actors/"+ strDir);
7181 pItem->m_bIsFolder=true;
7189 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7193 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
7199 if (NULL == m_pDB.get()) return;
7200 if (NULL == m_pDS.get()) return;
7202 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7203 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());
7205 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());
7206 m_pDS->query( strSQL.c_str() );
7208 while (!m_pDS->eof())
7210 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7211 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7217 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7218 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7219 pItem->SetPath("videodb://tvshows/actors/"+ strDir);
7220 pItem->m_bIsFolder=true;
7228 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7232 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
7238 if (NULL == m_pDB.get()) return;
7239 if (NULL == m_pDS.get()) return;
7242 if (!strSearch.empty())
7243 strLike = "and actors.strActor like '%%%s%%'";
7244 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7245 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());
7247 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
7248 m_pDS->query( strSQL.c_str() );
7250 while (!m_pDS->eof())
7252 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7253 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7259 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7260 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7261 pItem->SetPath("videodb://musicvideos/artists/"+ strDir);
7262 pItem->m_bIsFolder=true;
7270 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7274 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
7280 if (NULL == m_pDB.get()) return;
7281 if (NULL == m_pDS.get()) return;
7283 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7284 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());
7286 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
7287 m_pDS->query( strSQL.c_str() );
7289 while (!m_pDS->eof())
7291 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7292 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7298 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7299 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7300 pItem->SetPath("videodb://musicvideos/genres/"+ strDir);
7301 pItem->m_bIsFolder=true;
7309 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7313 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7319 if (NULL == m_pDB.get()) return;
7320 if (NULL == m_pDS.get()) return;
7323 if (!strSearch.empty())
7325 strLike = StringUtils::Format("and musicvideo.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
7326 strLike += "like '%%s%%%'";
7328 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7329 strSQL=PrepareSQL("select distinct musicvideo.c%02d,musicvideo.idMVideo,path.strPath from musicvideo,files,path where files.idFile=musicvideo.idFile and files.idPath=path.idPath"+strLike,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7332 if (!strLike.empty())
7333 strLike = "where "+strLike.Mid(4);
7334 strSQL=PrepareSQL("select distinct musicvideo.c%02d,musicvideo.idMVideo from musicvideo"+strLike,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7336 m_pDS->query( strSQL.c_str() );
7338 while (!m_pDS->eof())
7340 if (m_pDS->fv(0).get_asString().empty())
7346 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7347 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7353 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7354 CStdString strDir = StringUtils::Format("%ld", m_pDS->fv(1).get_asInt());
7355 pItem->SetPath("videodb://musicvideos/titles/"+ strDir);
7356 pItem->m_bIsFolder=false;
7364 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7368 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7374 if (NULL == m_pDB.get()) return;
7375 if (NULL == m_pDS.get()) return;
7377 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7378 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());
7380 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());
7381 m_pDS->query( strSQL.c_str() );
7383 while (!m_pDS->eof())
7385 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7386 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7392 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7393 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7395 pItem->SetPath("videodb://"+ strDir);
7396 pItem->m_bIsFolder=false;
7404 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7408 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7415 if (NULL == m_pDB.get()) return false;
7416 if (NULL == m_pDS.get()) return false;
7420 CStdString strSQL = "select %s from musicvideoview ";
7421 CVideoDbUrl videoUrl;
7422 CStdString strSQLExtra;
7423 Filter extFilter = filter;
7424 SortDescription sorting = sortDescription;
7425 if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7428 // Apply the limiting directly here if there's no special sorting but limiting
7429 if (extFilter.limit.empty() &&
7430 sorting.sortBy == SortByNone &&
7431 (sorting.limitStart > 0 || sorting.limitEnd > 0))
7433 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7434 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7437 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7439 int iRowsFound = RunQuery(strSQL);
7440 if (iRowsFound <= 0)
7441 return iRowsFound == 0;
7443 // store the total value of items as a property
7444 if (total < iRowsFound)
7446 items.SetProperty("total", total);
7448 DatabaseResults results;
7449 results.reserve(iRowsFound);
7450 if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7453 // get data from returned rows
7454 items.Reserve(results.size());
7455 // get songs from returned subtable
7456 const query_data &data = m_pDS->get_result_set().records;
7457 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7459 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7460 const dbiplus::sql_record* const record = data.at(targetRow);
7462 CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7463 if (!checkLocks || CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7464 g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
7466 CFileItemPtr item(new CFileItem(musicvideo));
7468 CVideoDbUrl itemUrl = videoUrl;
7469 CStdString path = StringUtils::Format("%ld", record->at(0).get_asInt());
7470 itemUrl.AppendPath(path);
7471 item->SetPath(itemUrl.ToString());
7473 item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7484 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7489 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7493 if (NULL == m_pDB.get()) return 0;
7494 if (NULL == m_pDS.get()) return 0;
7496 CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7497 if (!m_pDS->query(strSQL.c_str())) return 0;
7499 if (m_pDS->num_rows() == 0)
7504 songIDs.reserve(m_pDS->num_rows());
7505 while (!m_pDS->eof())
7507 songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7511 return songIDs.size();
7515 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7520 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7526 if (NULL == m_pDB.get()) return false;
7527 if (NULL == m_pDS.get()) return false;
7529 // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7530 CStdString strSQL = StringUtils::Format("select * from musicvideoview where %s", strWhere.c_str());
7531 strSQL += PrepareSQL(" order by RANDOM() limit 1");
7532 CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7534 if (!m_pDS->query(strSQL.c_str()))
7536 int iRowsFound = m_pDS->num_rows();
7537 if (iRowsFound != 1)
7542 *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7543 CStdString path = StringUtils::Format("videodb://musicvideos/titles/%ld",item->GetVideoInfoTag()->m_iDbId);
7544 item->SetPath(path);
7545 idSong = m_pDS->fv("idMVideo").get_asInt();
7546 item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7552 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7557 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7561 if (NULL == m_pDB.get()) return -1;
7562 if (NULL == m_pDS.get()) return -1;
7565 if (strAlbum.empty() && strTitle.empty())
7566 { // we want to return matching artists only
7567 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7568 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());
7570 strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7573 { // we want to return the matching musicvideo
7574 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7575 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());
7577 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());
7579 m_pDS->query( strSQL.c_str() );
7584 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7585 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7591 int lResult = m_pDS->fv(0).get_asInt();
7597 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7602 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7608 if (NULL == m_pDB.get()) return;
7609 if (NULL == m_pDS.get()) return;
7611 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7612 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());
7614 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());
7615 m_pDS->query( strSQL.c_str() );
7617 while (!m_pDS->eof())
7619 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7620 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7626 int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7627 int setId = m_pDS->fv("movie.idSet").get_asInt();
7628 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7630 if (setId <= 0 || !CSettings::Get().GetBool("videolibrary.groupmoviesets"))
7631 path = StringUtils::Format("videodb://movies/titles/%i", movieId);
7633 path = StringUtils::Format("videodb://movies/sets/%i/%i", setId, movieId);
7634 pItem->SetPath(path);
7635 pItem->m_bIsFolder=false;
7643 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7647 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7653 if (NULL == m_pDB.get()) return;
7654 if (NULL == m_pDS.get()) return;
7656 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7657 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());
7659 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());
7660 m_pDS->query( strSQL.c_str() );
7662 while (!m_pDS->eof())
7664 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7665 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7671 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7672 CStdString strDir = StringUtils::Format("tvshows/titles/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7674 pItem->SetPath("videodb://"+ strDir);
7675 pItem->m_bIsFolder=true;
7676 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7684 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7688 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7694 if (NULL == m_pDB.get()) return;
7695 if (NULL == m_pDS.get()) return;
7697 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7698 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());
7700 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());
7701 m_pDS->query( strSQL.c_str() );
7703 while (!m_pDS->eof())
7705 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7706 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7712 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7713 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());
7714 pItem->SetPath(path);
7715 pItem->m_bIsFolder=false;
7723 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7727 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7729 // Alternative searching - not quite as fast though due to
7730 // retrieving all information
7731 // 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()));
7732 // GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
7737 if (NULL == m_pDB.get()) return;
7738 if (NULL == m_pDS.get()) return;
7740 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7741 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());
7743 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());
7744 m_pDS->query( strSQL.c_str() );
7746 while (!m_pDS->eof())
7748 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7749 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7755 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7756 CStdString strDir = StringUtils::Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7758 pItem->SetPath("videodb://"+ strDir);
7759 pItem->m_bIsFolder=false;
7767 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7771 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7773 // Alternative searching - not quite as fast though due to
7774 // retrieving all information
7776 // 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());
7777 // 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());
7778 // GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
7784 if (NULL == m_pDB.get()) return;
7785 if (NULL == m_pDS.get()) return;
7787 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7788 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());
7790 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());
7791 m_pDS->query( strSQL.c_str() );
7793 while (!m_pDS->eof())
7795 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7796 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7802 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7803 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());
7804 pItem->SetPath(path);
7805 pItem->m_bIsFolder=false;
7813 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7817 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7823 if (NULL == m_pDB.get()) return;
7824 if (NULL == m_pDS.get()) return;
7826 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7827 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());
7829 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());
7831 m_pDS->query( strSQL.c_str() );
7833 while (!m_pDS->eof())
7835 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7836 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7842 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7843 CStdString path = StringUtils::Format("videodb://movies/titles/%ld", m_pDS->fv(0).get_asInt());
7844 pItem->SetPath(path);
7845 pItem->m_bIsFolder=false;
7855 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7859 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7865 if (NULL == m_pDB.get()) return;
7866 if (NULL == m_pDS.get()) return;
7868 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7869 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());
7871 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());
7873 m_pDS->query( strSQL.c_str() );
7875 while (!m_pDS->eof())
7877 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7878 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7884 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7885 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7887 pItem->SetPath("videodb://movies/directors/"+ strDir);
7888 pItem->m_bIsFolder=true;
7896 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7900 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7906 if (NULL == m_pDB.get()) return;
7907 if (NULL == m_pDS.get()) return;
7909 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7910 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());
7912 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());
7914 m_pDS->query( strSQL.c_str() );
7916 while (!m_pDS->eof())
7918 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7919 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7925 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7926 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7928 pItem->SetPath("videodb://tvshows/studios/"+ strDir);
7929 pItem->m_bIsFolder=true;
7937 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7941 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7947 if (NULL == m_pDB.get()) return;
7948 if (NULL == m_pDS.get()) return;
7950 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7951 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());
7953 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());
7955 m_pDS->query( strSQL.c_str() );
7957 while (!m_pDS->eof())
7959 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7960 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7966 CStdString strDir = StringUtils::Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7967 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7969 pItem->SetPath("videodb://musicvideos/albums/"+ strDir);
7970 pItem->m_bIsFolder=true;
7978 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7982 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7984 CGUIDialogProgress *progress=NULL;
7987 if (NULL == m_pDB.get()) return;
7988 if (NULL == m_pDS.get()) return;
7990 unsigned int time = XbmcThreads::SystemClockMillis();
7991 CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
7992 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
7996 // find all the files
8000 if (paths->size() == 0)
8002 RollbackTransaction();
8003 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8007 CStdString strPaths;
8008 for (std::set<int>::const_iterator i = paths->begin(); i != paths->end(); ++i)
8009 strPaths.AppendFormat(",%i",*i);
8010 sql = PrepareSQL("select * from files,path where files.idPath=path.idPath and path.idPath in (%s)",strPaths.Mid(1).c_str());
8013 sql = "select * from files, path where files.idPath = path.idPath";
8015 m_pDS->query(sql.c_str());
8016 if (m_pDS->num_rows() == 0) return;
8020 handle->SetTitle(g_localizeStrings.Get(700));
8021 handle->SetText("");
8023 else if (showProgress)
8025 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8028 progress->SetHeading(700);
8029 progress->SetLine(0, "");
8030 progress->SetLine(1, 313);
8031 progress->SetLine(2, 330);
8032 progress->SetPercentage(0);
8033 progress->StartModal();
8034 progress->ShowProgressBar(true);
8038 CStdString filesToDelete = "";
8039 CStdString moviesToDelete = "";
8040 CStdString episodesToDelete = "";
8041 CStdString musicVideosToDelete = "";
8043 std::vector<int> movieIDs;
8044 std::vector<int> episodeIDs;
8045 std::vector<int> musicVideoIDs;
8047 int total = m_pDS->num_rows();
8050 while (!m_pDS->eof())
8052 CStdString path = m_pDS->fv("path.strPath").get_asString();
8053 CStdString fileName = m_pDS->fv("files.strFileName").get_asString();
8054 CStdString fullPath;
8055 ConstructPath(fullPath,path,fileName);
8057 // get the first stacked file
8058 if (URIUtils::IsStack(fullPath))
8059 fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
8061 // remove optical, non-existing files
8062 if (URIUtils::IsOnDVD(fullPath) || !CFile::Exists(fullPath, false))
8063 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
8069 progress->SetPercentage(current * 100 / total);
8070 progress->Progress();
8071 if (progress->IsCanceled())
8075 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8081 handle->SetPercentage(current/(float)total*100);
8088 // Add any files that don't have a valid idPath entry to the filesToDelete list.
8089 sql = "select files.idFile from files where idPath not in (select idPath from path)";
8090 m_pDS->query(sql.c_str());
8091 while (!m_pDS->eof())
8093 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
8098 if ( ! filesToDelete.empty() )
8100 filesToDelete.TrimRight(",");
8101 // now grab them movies
8102 sql = PrepareSQL("select idMovie from movie where idFile in (%s)",filesToDelete.c_str());
8103 m_pDS->query(sql.c_str());
8104 while (!m_pDS->eof())
8106 movieIDs.push_back(m_pDS->fv(0).get_asInt());
8107 moviesToDelete += m_pDS->fv(0).get_asString() + ",";
8111 // now grab them episodes
8112 sql = PrepareSQL("select idEpisode from episode where idFile in (%s)",filesToDelete.c_str());
8113 m_pDS->query(sql.c_str());
8114 while (!m_pDS->eof())
8116 episodeIDs.push_back(m_pDS->fv(0).get_asInt());
8117 episodesToDelete += m_pDS->fv(0).get_asString() + ",";
8122 // now grab them musicvideos
8123 sql = PrepareSQL("select idMVideo from musicvideo where idFile in (%s)",filesToDelete.c_str());
8124 m_pDS->query(sql.c_str());
8125 while (!m_pDS->eof())
8127 musicVideoIDs.push_back(m_pDS->fv(0).get_asInt());
8128 musicVideosToDelete += m_pDS->fv(0).get_asString() + ",";
8136 progress->SetPercentage(100);
8137 progress->Progress();
8140 if ( ! filesToDelete.empty() )
8142 filesToDelete = "(" + filesToDelete + ")";
8143 CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
8144 sql = "delete from files where idFile in " + filesToDelete;
8145 m_pDS->exec(sql.c_str());
8147 CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
8148 sql = "delete from streamdetails where idFile in " + filesToDelete;
8149 m_pDS->exec(sql.c_str());
8151 CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
8152 sql = "delete from bookmark where idFile in " + filesToDelete;
8153 m_pDS->exec(sql.c_str());
8155 CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
8156 sql = "delete from settings where idFile in " + filesToDelete;
8157 m_pDS->exec(sql.c_str());
8159 CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
8160 sql = "delete from stacktimes where idFile in " + filesToDelete;
8161 m_pDS->exec(sql.c_str());
8164 if ( ! moviesToDelete.empty() )
8166 moviesToDelete = "(" + moviesToDelete.TrimRight(",") + ")";
8168 CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
8169 sql = "delete from movie where idMovie in " + moviesToDelete;
8170 m_pDS->exec(sql.c_str());
8172 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
8173 sql = "delete from actorlinkmovie where idMovie in " + moviesToDelete;
8174 m_pDS->exec(sql.c_str());
8176 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
8177 sql = "delete from directorlinkmovie where idMovie in " + moviesToDelete;
8178 m_pDS->exec(sql.c_str());
8180 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
8181 sql = "delete from writerlinkmovie where idMovie in " + moviesToDelete;
8182 m_pDS->exec(sql.c_str());
8184 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
8185 sql = "delete from genrelinkmovie where idMovie in " + moviesToDelete;
8186 m_pDS->exec(sql.c_str());
8188 CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
8189 sql = "delete from countrylinkmovie where idMovie in " + moviesToDelete;
8190 m_pDS->exec(sql.c_str());
8192 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
8193 sql = "delete from studiolinkmovie where idMovie in " + moviesToDelete;
8194 m_pDS->exec(sql.c_str());
8197 if ( ! episodesToDelete.empty() )
8199 episodesToDelete = "(" + episodesToDelete.TrimRight(",") + ")";
8201 CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
8202 sql = "delete from episode where idEpisode in " + episodesToDelete;
8203 m_pDS->exec(sql.c_str());
8205 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
8206 sql = "delete from actorlinkepisode where idEpisode in " + episodesToDelete;
8207 m_pDS->exec(sql.c_str());
8209 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
8210 sql = "delete from directorlinkepisode where idEpisode in " + episodesToDelete;
8211 m_pDS->exec(sql.c_str());
8213 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
8214 sql = "delete from writerlinkepisode where idEpisode in " + episodesToDelete;
8215 m_pDS->exec(sql.c_str());
8218 CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and have content set...", __FUNCTION__);
8219 sql = "select * from path where not (strContent='' and strSettings='' and strHash='' and exclude!=1)";
8220 m_pDS->query(sql.c_str());
8222 while (!m_pDS->eof())
8224 if (!CDirectory::Exists(m_pDS->fv("path.strPath").get_asString()))
8225 strIds.AppendFormat("%i,", m_pDS->fv("path.idPath").get_asInt());
8229 if (!strIds.empty())
8231 strIds.TrimRight(",");
8232 sql = PrepareSQL("delete from path where idPath in (%s)",strIds.c_str());
8233 m_pDS->exec(sql.c_str());
8234 sql = PrepareSQL("delete from tvshowlinkpath where idPath in (%s)",strIds.c_str());
8235 m_pDS->exec(sql.c_str());
8237 sql = "delete from tvshowlinkpath where idPath not in (select idPath from path)";
8238 m_pDS->exec(sql.c_str());
8240 CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
8241 sql = "delete from tvshow where idShow not in (select idShow from tvshowlinkpath)";
8242 m_pDS->exec(sql.c_str());
8244 std::vector<int> tvshowIDs;
8245 CStdString showsToDelete;
8246 sql = "select tvshow.idShow from tvshow "
8247 "join tvshowlinkpath on tvshow.idShow=tvshowlinkpath.idShow "
8248 "join path on path.idPath=tvshowlinkpath.idPath "
8249 "where tvshow.idShow not in (select idShow from episode) "
8250 "and path.strContent=''";
8251 m_pDS->query(sql.c_str());
8252 while (!m_pDS->eof())
8254 tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
8255 showsToDelete += m_pDS->fv(0).get_asString() + ",";
8259 if (!showsToDelete.empty())
8261 sql = "delete from tvshow where idShow in (" + showsToDelete.TrimRight(",") + ")";
8262 m_pDS->exec(sql.c_str());
8265 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
8266 sql = "delete from actorlinktvshow where idShow not in (select idShow from tvshow)";
8267 m_pDS->exec(sql.c_str());
8269 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
8270 sql = "delete from directorlinktvshow where idShow not in (select idShow from tvshow)";
8271 m_pDS->exec(sql.c_str());
8273 CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
8274 sql = "delete from tvshowlinkpath where idShow not in (select idShow from tvshow)";
8275 m_pDS->exec(sql.c_str());
8277 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
8278 sql = "delete from genrelinktvshow where idShow not in (select idShow from tvshow)";
8279 m_pDS->exec(sql.c_str());
8281 CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
8282 sql = "delete from seasons where idShow not in (select idShow from tvshow)";
8283 m_pDS->exec(sql.c_str());
8285 CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
8286 sql = "delete from movielinktvshow where idShow not in (select idShow from tvshow)";
8287 m_pDS->exec(sql.c_str());
8288 sql = "delete from movielinktvshow where idMovie not in (select distinct idMovie from movie)";
8289 m_pDS->exec(sql.c_str());
8291 if ( ! musicVideosToDelete.empty() )
8293 musicVideosToDelete = "(" + musicVideosToDelete.TrimRight(",") + ")";
8295 CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8296 sql = "delete from musicvideo where idMVideo in " + musicVideosToDelete;
8297 m_pDS->exec(sql.c_str());
8299 CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8300 sql = "delete from artistlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8301 m_pDS->exec(sql.c_str());
8303 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8304 sql = "delete from directorlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8305 m_pDS->exec(sql.c_str());
8307 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8308 sql = "delete from genrelinkmusicvideo where idMVideo in " + musicVideosToDelete;
8309 m_pDS->exec(sql.c_str());
8311 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8312 sql = "delete from studiolinkmusicvideo where idMVideo in " + musicVideosToDelete;
8313 m_pDS->exec(sql.c_str());
8316 CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8317 sql = StringUtils::Format("delete from path where strContent='' and strSettings='' and strHash='' and exclude!=1 "
8318 "and idPath not in (select distinct idPath from files) "
8319 "and idPath not in (select distinct idPath from tvshowlinkpath) "
8320 "and idPath not in (select distinct c%02d from movie) "
8321 "and idPath not in (select distinct c%02d from tvshow) "
8322 "and idPath not in (select distinct c%02d from episode) "
8323 "and idPath not in (select distinct c%02d from musicvideo)"
8324 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8325 m_pDS->exec(sql.c_str());
8327 CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8328 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)";
8329 m_pDS->exec(sql.c_str());
8331 CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8332 sql = "delete from country where idCountry not in (select distinct idCountry from countrylinkmovie)";
8333 m_pDS->exec(sql.c_str());
8335 CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8336 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)";
8337 m_pDS->exec(sql.c_str());
8339 CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8340 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)";
8341 m_pDS->exec(sql.c_str());
8343 CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8344 sql = "delete from sets where idSet not in (select distinct idSet from movie)";
8345 m_pDS->exec(sql.c_str());
8347 CommitTransaction();
8350 handle->SetTitle(g_localizeStrings.Get(331));
8354 CUtil::DeleteVideoDatabaseDirectoryCache();
8356 time = XbmcThreads::SystemClockMillis() - time;
8357 CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8359 for (unsigned int i = 0; i < movieIDs.size(); i++)
8360 AnnounceRemove("movie", movieIDs[i]);
8362 for (unsigned int i = 0; i < episodeIDs.size(); i++)
8363 AnnounceRemove("episode", episodeIDs[i]);
8365 for (unsigned int i = 0; i < tvshowIDs.size(); i++)
8366 AnnounceRemove("tvshow", tvshowIDs[i]);
8368 for (unsigned int i = 0; i < musicVideoIDs.size(); i++)
8369 AnnounceRemove("musicvideo", musicVideoIDs[i]);
8373 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8374 RollbackTransaction();
8379 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8382 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8385 CFileItemList items;
8386 GetTvShowsByWhere("videodb://tvshows/titles/", "", items);
8387 CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8388 CDirectory::Create(showPath);
8389 for (int i = 0; i < items.Size(); i++)
8391 // create a folder in this directory
8392 CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8393 CStdString TVFolder = URIUtils::AddFileToFolder(showPath, showName);
8394 if (CDirectory::Create(TVFolder))
8395 { // right - grab the episodes and dump them as well
8396 CFileItemList episodes;
8397 Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8398 GetEpisodesByWhere("videodb://tvshows/titles/", filter, episodes);
8399 for (int i = 0; i < episodes.Size(); i++)
8401 CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8402 CStdString episode = StringUtils::Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8404 CStdString episodePath = URIUtils::AddFileToFolder(TVFolder, episode);
8406 if (file.OpenForWrite(episodePath))
8413 GetMoviesByWhere("videodb://movies/titles/", "", items);
8414 CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8415 CDirectory::Create(moviePath);
8416 for (int i = 0; i < items.Size(); i++)
8418 CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8419 CStdString movie = StringUtils::Format("%s.avi", tag->m_strTitle.c_str());
8421 if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8426 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8428 CGUIDialogProgress *progress=NULL;
8431 if (NULL == m_pDB.get()) return;
8432 if (NULL == m_pDS.get()) return;
8433 if (NULL == m_pDS2.get()) return;
8435 // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8436 auto_ptr<Dataset> pDS;
8437 pDS.reset(m_pDB->CreateDataset());
8438 if (NULL == pDS.get()) return;
8440 auto_ptr<Dataset> pDS2;
8441 pDS2.reset(m_pDB->CreateDataset());
8442 if (NULL == pDS2.get()) return;
8444 // if we're exporting to a single folder, we export thumbs as well
8445 CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8446 CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8447 CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8448 CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8449 CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8450 CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8456 CDirectory::Remove(exportRoot);
8457 CDirectory::Create(exportRoot);
8458 CDirectory::Create(actorsDir);
8459 CDirectory::Create(moviesDir);
8460 CDirectory::Create(musicvideosDir);
8461 CDirectory::Create(tvshowsDir);
8464 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8466 CStdString sql = "select * from movieview";
8468 m_pDS->query(sql.c_str());
8472 progress->SetHeading(647);
8473 progress->SetLine(0, 650);
8474 progress->SetLine(1, "");
8475 progress->SetLine(2, "");
8476 progress->SetPercentage(0);
8477 progress->StartModal();
8478 progress->ShowProgressBar(true);
8481 int total = m_pDS->num_rows();
8484 // create our xml document
8485 CXBMCTinyXML xmlDoc;
8486 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8487 xmlDoc.InsertEndChild(decl);
8488 TiXmlNode *pMain = NULL;
8493 TiXmlElement xmlMainElement("videodb");
8494 pMain = xmlDoc.InsertEndChild(xmlMainElement);
8495 XMLUtils::SetInt(pMain,"version", GetExportVersion());
8498 while (!m_pDS->eof())
8500 CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8501 // strip paths to make them relative
8502 if (movie.m_strTrailer.Mid(0,movie.m_strPath.size()).Equals(movie.m_strPath))
8503 movie.m_strTrailer = movie.m_strTrailer.Mid(movie.m_strPath.size());
8504 map<string, string> artwork;
8505 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8507 TiXmlElement additionalNode("art");
8508 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8509 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8510 movie.Save(pMain, "movie", true, &additionalNode);
8513 movie.Save(pMain, "movie", !singleFiles);
8515 // reset old skip state
8520 progress->SetLine(1, movie.m_strTitle);
8521 progress->SetPercentage(current * 100 / total);
8522 progress->Progress();
8523 if (progress->IsCanceled())
8531 CFileItem item(movie.m_strFileNameAndPath,false);
8532 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8534 if (!item.Exists(false))
8536 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8541 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8543 if (item.IsOpticalMediaFile())
8545 nfoFile = URIUtils::AddFileToFolder(
8546 URIUtils::GetParentPath(nfoFile),
8547 URIUtils::GetFileName(nfoFile));
8550 if (overwrite || !CFile::Exists(nfoFile, false))
8552 if(!xmlDoc.SaveFile(nfoFile))
8554 CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8555 bSkip = ExportSkipEntry(nfoFile);
8572 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8573 xmlDoc.InsertEndChild(decl);
8576 if (images && !bSkip)
8580 CStdString strFileName(movie.m_strTitle);
8581 if (movie.m_iYear > 0)
8582 strFileName.AppendFormat("_%i", movie.m_iYear);
8583 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8585 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8587 CStdString savedThumb = item.GetLocalArt(i->first, false);
8588 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8591 ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8598 // find all musicvideos
8599 sql = "select * from musicvideoview";
8601 m_pDS->query(sql.c_str());
8603 total = m_pDS->num_rows();
8606 while (!m_pDS->eof())
8608 CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8609 map<string, string> artwork;
8610 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8612 TiXmlElement additionalNode("art");
8613 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8614 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8615 movie.Save(pMain, "musicvideo", true, &additionalNode);
8618 movie.Save(pMain, "musicvideo", !singleFiles);
8620 // reset old skip state
8625 progress->SetLine(1, movie.m_strTitle);
8626 progress->SetPercentage(current * 100 / total);
8627 progress->Progress();
8628 if (progress->IsCanceled())
8636 CFileItem item(movie.m_strFileNameAndPath,false);
8637 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8639 if (!item.Exists(false))
8641 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8646 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8648 if (overwrite || !CFile::Exists(nfoFile, false))
8650 if(!xmlDoc.SaveFile(nfoFile))
8652 CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8653 bSkip = ExportSkipEntry(nfoFile);
8670 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8671 xmlDoc.InsertEndChild(decl);
8673 if (images && !bSkip)
8677 CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8678 if (movie.m_iYear > 0)
8679 strFileName.AppendFormat("_%i", movie.m_iYear);
8680 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8682 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8684 CStdString savedThumb = item.GetLocalArt(i->first, false);
8685 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8693 // repeat for all tvshows
8694 sql = "SELECT * FROM tvshowview";
8695 m_pDS->query(sql.c_str());
8697 total = m_pDS->num_rows();
8700 while (!m_pDS->eof())
8702 CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8704 map<int, map<string, string> > seasonArt;
8705 GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8707 map<string, string> artwork;
8708 if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8710 TiXmlElement additionalNode("art");
8711 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8712 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8713 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8715 TiXmlElement seasonNode("season");
8716 seasonNode.SetAttribute("num", i->first);
8717 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8718 XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8719 additionalNode.InsertEndChild(seasonNode);
8721 tvshow.Save(pMain, "tvshow", true, &additionalNode);
8724 tvshow.Save(pMain, "tvshow", !singleFiles);
8726 // reset old skip state
8731 progress->SetLine(1, tvshow.m_strTitle);
8732 progress->SetPercentage(current * 100 / total);
8733 progress->Progress();
8734 if (progress->IsCanceled())
8742 // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8743 if (URIUtils::IsMultiPath(tvshow.m_strPath))
8744 tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8746 CFileItem item(tvshow.m_strPath, true);
8747 if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8749 if (!item.Exists(false))
8751 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8756 CStdString nfoFile = URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo");
8758 if (overwrite || !CFile::Exists(nfoFile, false))
8760 if(!xmlDoc.SaveFile(nfoFile))
8762 CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8763 bSkip = ExportSkipEntry(nfoFile);
8780 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8781 xmlDoc.InsertEndChild(decl);
8783 if (images && !bSkip)
8786 item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8788 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8790 CStdString savedThumb = item.GetLocalArt(i->first, true);
8791 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8795 ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8797 // export season thumbs
8798 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8802 seasonThumb = "season-all";
8803 else if (i->first == 0)
8804 seasonThumb = "season-specials";
8806 seasonThumb = StringUtils::Format("season%02i", i->first);
8807 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8809 CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8810 if (!i->second.empty())
8811 CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8816 // now save the episodes from this show
8817 sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8818 pDS->query(sql.c_str());
8819 CStdString showDir(item.GetPath());
8823 CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8824 map<string, string> artwork;
8825 if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8827 TiXmlElement additionalNode("art");
8828 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8829 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8830 episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8832 else if (!singleFiles)
8833 episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8835 episode.Save(pMain, "episodedetails", !singleFiles);
8837 // multi-episode files need dumping to the same XML
8838 while (singleFiles && !pDS->eof() &&
8839 episode.m_iFileId == pDS->fv("idFile").get_asInt())
8841 episode = GetDetailsForEpisode(pDS, true);
8842 episode.Save(pMain, "episodedetails", !singleFiles);
8846 // reset old skip state
8849 CFileItem item(episode.m_strFileNameAndPath, false);
8850 if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8852 if (!item.Exists(false))
8854 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8859 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8861 if (overwrite || !CFile::Exists(nfoFile, false))
8863 if(!xmlDoc.SaveFile(nfoFile))
8865 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8866 bSkip = ExportSkipEntry(nfoFile);
8883 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8884 xmlDoc.InsertEndChild(decl);
8887 if (images && !bSkip)
8891 CStdString epName = StringUtils::Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8892 item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8894 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8896 CStdString savedThumb = item.GetLocalArt(i->first, false);
8897 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8900 ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8909 if (singleFiles && progress)
8911 progress->SetPercentage(100);
8912 progress->Progress();
8917 // now dump path info
8918 set<CStdString> paths;
8920 TiXmlElement xmlPathElement("paths");
8921 TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8922 for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8924 bool foundDirectly = false;
8925 SScanSettings settings;
8926 ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8927 if (info && foundDirectly)
8929 TiXmlElement xmlPathElement2("path");
8930 TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8931 XMLUtils::SetString(pPath,"url", *iter);
8932 XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8933 XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8934 XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8935 XMLUtils::SetString(pPath,"scraperpath", info->ID());
8938 xmlDoc.SaveFile(xmlFile);
8943 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8950 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8952 CStdString strPath(strDir);
8955 strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8956 if (!CDirectory::Exists(strPath))
8958 CDirectory::Create(strPath);
8959 CFile::SetHidden(strPath, true);
8963 for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8966 item.SetLabel(iter->strName);
8967 if (!iter->thumb.empty())
8969 CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8970 CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8975 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8977 CStdString strParent;
8978 URIUtils::GetParentPath(nfoFile,strParent);
8979 CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8981 bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8984 CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8986 CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8991 void CVideoDatabase::ImportFromXML(const CStdString &path)
8993 CGUIDialogProgress *progress=NULL;
8996 if (NULL == m_pDB.get()) return;
8997 if (NULL == m_pDS.get()) return;
8999 CXBMCTinyXML xmlDoc;
9000 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
9003 TiXmlElement *root = xmlDoc.RootElement();
9006 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
9009 progress->SetHeading(648);
9010 progress->SetLine(0, 649);
9011 progress->SetLine(1, 330);
9012 progress->SetLine(2, "");
9013 progress->SetPercentage(0);
9014 progress->StartModal();
9015 progress->ShowProgressBar(true);
9019 XMLUtils::GetInt(root, "version", iVersion);
9021 CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
9024 TiXmlElement *movie = root->FirstChildElement();
9027 // first count the number of items...
9030 if (strnicmp(movie->Value(), "movie", 5)==0 ||
9031 strnicmp(movie->Value(), "tvshow", 6)==0 ||
9032 strnicmp(movie->Value(), "musicvideo",10)==0 )
9034 movie = movie->NextSiblingElement();
9037 CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
9038 CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
9039 CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
9040 CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
9041 CVideoInfoScanner scanner;
9042 // add paths first (so we have scraper settings available)
9043 TiXmlElement *path = root->FirstChildElement("paths");
9044 path = path->FirstChildElement();
9048 if (XMLUtils::GetString(path,"url",strPath) && !strPath.empty())
9052 if (XMLUtils::GetString(path,"content", content) && !content.empty())
9053 { // check the scraper exists, if so store the path
9056 XMLUtils::GetString(path,"scraperpath",id);
9057 if (CAddonMgr::Get().GetAddon(id, addon))
9059 SScanSettings settings;
9060 ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
9061 // FIXME: scraper settings are not exported?
9062 scraper->SetPathSettings(TranslateContent(content), "");
9063 XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
9064 XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
9065 SetScraperForPath(strPath,scraper,settings);
9068 path = path->NextSiblingElement();
9070 movie = root->FirstChildElement();
9074 if (strnicmp(movie->Value(), "movie", 5) == 0)
9077 CFileItem item(info);
9078 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
9079 CStdString filename = info.m_strTitle;
9080 if (info.m_iYear > 0)
9081 filename.AppendFormat("_%i", info.m_iYear);
9082 CFileItem artItem(item);
9083 artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
9084 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9085 item.SetArt(artItem.GetArt());
9086 scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
9089 else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
9092 CFileItem item(info);
9093 bool useFolders = info.m_basePath.empty() ? LookupByFolders(item.GetPath()) : false;
9094 CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
9095 if (info.m_iYear > 0)
9096 filename.AppendFormat("_%i", info.m_iYear);
9097 CFileItem artItem(item);
9098 artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
9099 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9100 item.SetArt(artItem.GetArt());
9101 scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
9104 else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
9106 // load the TV show in. NOTE: This deletes all episodes under the TV Show, which may not be
9107 // what we desire. It may make better sense to only delete (or even better, update) the show information
9109 URIUtils::AddSlashAtEnd(info.m_strPath);
9110 DeleteTvShow(info.m_strPath);
9111 CFileItem showItem(info);
9112 bool useFolders = info.m_basePath.empty() ? LookupByFolders(showItem.GetPath(), true) : false;
9113 CFileItem artItem(showItem);
9114 CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
9115 artItem.SetPath(artPath);
9116 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9117 showItem.SetArt(artItem.GetArt());
9118 int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
9120 map<int, map<string, string> > seasonArt;
9121 artItem.GetVideoInfoTag()->m_strPath = artPath;
9122 scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
9123 for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
9125 int seasonID = AddSeason(showID, i->first);
9126 SetArtForItem(seasonID, "season", i->second);
9129 // now load the episodes
9130 TiXmlElement *episode = movie->FirstChildElement("episodedetails");
9133 // no need to delete the episode info, due to the above deletion
9136 CFileItem item(info);
9137 CStdString filename = StringUtils::Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
9138 CFileItem artItem(item);
9139 artItem.SetPath(GetSafeFile(artPath, filename));
9140 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9141 item.SetArt(artItem.GetArt());
9142 scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
9143 episode = episode->NextSiblingElement("episodedetails");
9146 movie = movie->NextSiblingElement();
9147 if (progress && total)
9149 progress->SetPercentage(current * 100 / total);
9150 progress->SetLine(2, info.m_strTitle);
9151 progress->Progress();
9152 if (progress->IsCanceled())
9155 RollbackTransaction();
9161 CommitTransaction();
9165 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
9166 RollbackTransaction();
9172 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
9174 if (!node) return false;
9175 const TiXmlNode *art = node->FirstChild();
9176 while (art && art->FirstChild())
9178 artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
9179 art = art->NextSibling();
9181 return !artwork.empty();
9184 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
9186 if (URIUtils::IsStack(strFileName) ||
9187 URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
9188 strDest = strFileName;
9190 strDest = URIUtils::AddFileToFolder(strPath, strFileName);
9193 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
9195 if (URIUtils::IsStack(strFileNameAndPath) || StringUtils::StartsWithNoCase(strFileNameAndPath, "rar://") || StringUtils::StartsWithNoCase(strFileNameAndPath, "zip://"))
9197 URIUtils::GetParentPath(strFileNameAndPath,strPath);
9198 strFileName = strFileNameAndPath;
9200 else if (URIUtils::IsPlugin(strFileNameAndPath))
9202 CURL url(strFileNameAndPath);
9203 strPath = url.GetWithoutFilename();
9204 strFileName = strFileNameAndPath;
9207 URIUtils::Split(strFileNameAndPath,strPath, strFileName);
9210 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
9212 SScanSettings settings;
9214 ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
9215 SetPathHash(strPath,"");
9218 if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
9220 if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
9222 CStdString strParent;
9223 URIUtils::GetParentPath(strPath,strParent);
9224 SetPathHash(strParent,"");
9229 bool CVideoDatabase::CommitTransaction()
9231 if (CDatabase::CommitTransaction())
9232 { // number of items in the db has likely changed, so recalculate
9233 g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
9234 g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
9235 g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
9241 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, int dbField, const std::string &strValue)
9246 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9249 string strTable, strField;
9250 if (type == VIDEODB_CONTENT_MOVIES)
9253 strField = "idMovie";
9255 else if (type == VIDEODB_CONTENT_TVSHOWS)
9257 strTable = "tvshow";
9258 strField = "idShow";
9260 else if (type == VIDEODB_CONTENT_EPISODES)
9262 strTable = "episode";
9263 strField = "idEpisode";
9265 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
9267 strTable = "musicvideo";
9268 strField = "idMVideo";
9271 if (strTable.empty())
9274 return SetSingleValue(strTable, StringUtils::Format("c%02u", dbField), strValue, strField, dbId);
9278 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
9283 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, Field dbField, const std::string &strValue)
9285 MediaType mediaType = DatabaseUtils::MediaTypeFromVideoContentType(type);
9286 if (mediaType == MediaTypeNone)
9289 int dbFieldIndex = DatabaseUtils::GetField(dbField, mediaType);
9290 if (dbFieldIndex < 0)
9293 return SetSingleValue(type, dbId, dbFieldIndex, strValue);
9296 bool CVideoDatabase::SetSingleValue(const std::string &table, const std::string &fieldName, const std::string &strValue,
9297 const std::string &conditionName /* = "" */, int conditionValue /* = -1 */)
9299 if (table.empty() || fieldName.empty())
9305 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9308 sql = PrepareSQL("UPDATE %s SET %s='%s'", table.c_str(), fieldName.c_str(), strValue.c_str());
9309 if (!conditionName.empty())
9310 sql += PrepareSQL(" WHERE %s=%u", conditionName.c_str(), conditionValue);
9311 if (m_pDS->exec(sql.c_str()) == 0)
9316 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, sql.c_str());
9321 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
9323 CStdString safeThumb(name);
9324 safeThumb.Replace(' ', '_');
9325 return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
9328 void CVideoDatabase::AnnounceRemove(std::string content, int id)
9331 data["type"] = content;
9333 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9336 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9339 data["type"] = content;
9341 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9344 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9346 CStdString path(strPath);
9348 if(URIUtils::IsMultiPath(path))
9350 vector<CStdString> paths;
9351 CMultiPathDirectory::GetPaths(path, paths);
9353 for(unsigned i=0;i<paths.size();i++)
9354 GetItemsForPath(content, paths[i], items);
9356 return items.Size() > 0;
9359 int pathID = GetPathId(path);
9363 if (content == "movies")
9365 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9366 GetMoviesByWhere("videodb://movies/titles/", filter, items);
9368 else if (content == "episodes")
9370 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9371 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
9373 else if (content == "tvshows")
9375 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9376 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
9378 else if (content == "musicvideos")
9380 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9381 GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
9383 for (int i = 0; i < items.Size(); i++)
9384 items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9385 return items.Size() > 0;
9388 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9390 if (!videoUrl.IsValid())
9393 std::string type = videoUrl.GetType();
9394 std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9395 const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9396 CUrlOptions::UrlOptions::const_iterator option;
9398 if (type == "movies")
9400 option = options.find("genreid");
9401 if (option != options.end())
9403 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9404 filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9407 option = options.find("genre");
9408 if (option != options.end())
9410 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9411 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9414 option = options.find("countryid");
9415 if (option != options.end())
9417 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9418 filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9421 option = options.find("country");
9422 if (option != options.end())
9424 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9425 filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9428 option = options.find("studioid");
9429 if (option != options.end())
9431 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9432 filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9435 option = options.find("studio");
9436 if (option != options.end())
9438 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9439 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9442 option = options.find("directorid");
9443 if (option != options.end())
9445 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9446 filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9449 option = options.find("director");
9450 if (option != options.end())
9452 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9453 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9456 option = options.find("year");
9457 if (option != options.end())
9458 filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9460 option = options.find("actorid");
9461 if (option != options.end())
9463 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9464 filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9467 option = options.find("actor");
9468 if (option != options.end())
9470 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9471 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9474 option = options.find("setid");
9475 if (option != options.end())
9476 filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9478 option = options.find("set");
9479 if (option != options.end())
9481 filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9482 filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9485 option = options.find("tagid");
9486 if (option != options.end())
9488 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9489 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9492 option = options.find("tag");
9493 if (option != options.end())
9495 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9496 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9499 else if (type == "tvshows")
9501 if (itemType == "tvshows")
9503 option = options.find("genreid");
9504 if (option != options.end())
9506 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9507 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9510 option = options.find("genre");
9511 if (option != options.end())
9513 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9514 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9517 option = options.find("studioid");
9518 if (option != options.end())
9520 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9521 filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9524 option = options.find("studio");
9525 if (option != options.end())
9527 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9528 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9531 option = options.find("directorid");
9532 if (option != options.end())
9534 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9535 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9538 option = options.find("year");
9539 if (option != options.end())
9540 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9542 option = options.find("actorid");
9543 if (option != options.end())
9545 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9546 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9549 option = options.find("actor");
9550 if (option != options.end())
9552 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9553 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9556 option = options.find("tagid");
9557 if (option != options.end())
9559 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9560 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9563 option = options.find("tag");
9564 if (option != options.end())
9566 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9567 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9570 else if (itemType == "seasons")
9572 option = options.find("genreid");
9573 if (option != options.end())
9575 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9576 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9579 option = options.find("directorid");
9580 if (option != options.end())
9582 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9583 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9586 option = options.find("year");
9587 if (option != options.end())
9588 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9590 option = options.find("actorid");
9591 if (option != options.end())
9593 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9594 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9597 else if (itemType == "episodes")
9600 option = options.find("tvshowid");
9601 if (option != options.end())
9602 idShow = (int)option->second.asInteger();
9605 option = options.find("season");
9606 if (option != options.end())
9607 season = (int)option->second.asInteger();
9609 CStdString strIn = PrepareSQL("= %i", idShow);
9610 GetStackedTvShowList(idShow, strIn);
9614 bool condition = false;
9616 option = options.find("genreid");
9617 if (option != options.end())
9620 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9621 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9624 option = options.find("genre");
9625 if (option != options.end())
9628 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9629 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9632 option = options.find("directorid");
9633 if (option != options.end())
9636 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9637 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9640 option = options.find("director");
9641 if (option != options.end())
9644 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9645 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9648 option = options.find("year");
9649 if (option != options.end())
9652 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9655 option = options.find("actorid");
9656 if (option != options.end())
9659 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9660 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9663 option = options.find("actor");
9664 if (option != options.end())
9667 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9668 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9672 filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9676 if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9677 filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9679 filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9680 VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9685 option = options.find("year");
9686 if (option != options.end())
9687 filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9689 option = options.find("directorid");
9690 if (option != options.end())
9692 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9693 filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9696 option = options.find("director");
9697 if (option != options.end())
9699 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9700 filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9705 else if (type == "musicvideos")
9707 option = options.find("genreid");
9708 if (option != options.end())
9710 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9711 filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9714 option = options.find("genre");
9715 if (option != options.end())
9717 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9718 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9721 option = options.find("studioid");
9722 if (option != options.end())
9724 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9725 filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9728 option = options.find("studio");
9729 if (option != options.end())
9731 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9732 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9735 option = options.find("directorid");
9736 if (option != options.end())
9738 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9739 filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9742 option = options.find("director");
9743 if (option != options.end())
9745 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9746 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9749 option = options.find("year");
9750 if (option != options.end())
9751 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9753 option = options.find("artistid");
9754 if (option != options.end())
9756 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9757 filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9760 option = options.find("artist");
9761 if (option != options.end())
9763 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9764 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9767 option = options.find("albumid");
9768 if (option != options.end())
9769 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()));
9771 option = options.find("tagid");
9772 if (option != options.end())
9774 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9775 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9778 option = options.find("tag");
9779 if (option != options.end())
9781 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9782 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9788 option = options.find("xsp");
9789 if (option != options.end())
9792 if (!xsp.LoadFromJson(option->second.asString()))
9795 // check if the filter playlist matches the item type
9796 if (xsp.GetType() == itemType ||
9797 (xsp.GetGroup() == itemType && !xsp.IsGroupMixed()) ||
9798 // handle episode listings with videodb://tvshows/titles/ which get the rest
9799 // of the path (season and episodeid) appended later
9800 (xsp.GetType() == "episodes" && itemType == "tvshows"))
9802 std::set<CStdString> playlists;
9803 filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9805 if (xsp.GetLimit() > 0)
9806 sorting.limitEnd = xsp.GetLimit();
9807 if (xsp.GetOrder() != SortByNone)
9808 sorting.sortBy = xsp.GetOrder();
9809 if (xsp.GetOrderDirection() != SortOrderNone)
9810 sorting.sortOrder = xsp.GetOrderDirection();
9811 if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
9812 sorting.sortAttributes = SortAttributeIgnoreArticle;
9816 option = options.find("filter");
9817 if (option != options.end())
9819 CSmartPlaylist xspFilter;
9820 if (!xspFilter.LoadFromJson(option->second.asString()))
9823 // check if the filter playlist matches the item type
9824 if (xspFilter.GetType() == itemType)
9826 std::set<CStdString> playlists;
9827 filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9829 // remove the filter if it doesn't match the item type
9831 videoUrl.RemoveOption("filter");