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";
136 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
139 column.Format(",c%02d text", i);
142 columns += ", idSet integer)";
143 m_pDS->exec(columns.c_str());
144 m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
145 m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
147 CLog::Log(LOGINFO, "create actorlinkmovie table");
148 m_pDS->exec("CREATE TABLE actorlinkmovie ( idActor integer, idMovie integer, strRole text, iOrder integer)\n");
149 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_1 ON actorlinkmovie ( idActor, idMovie )\n");
150 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_2 ON actorlinkmovie ( idMovie, idActor )\n");
152 CLog::Log(LOGINFO, "create directorlinkmovie table");
153 m_pDS->exec("CREATE TABLE directorlinkmovie ( idDirector integer, idMovie integer)\n");
154 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_1 ON directorlinkmovie ( idDirector, idMovie )\n");
155 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_2 ON directorlinkmovie ( idMovie, idDirector )\n");
157 CLog::Log(LOGINFO, "create writerlinkmovie table");
158 m_pDS->exec("CREATE TABLE writerlinkmovie ( idWriter integer, idMovie integer)\n");
159 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_1 ON writerlinkmovie ( idWriter, idMovie )\n");
160 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_2 ON writerlinkmovie ( idMovie, idWriter )\n");
162 CLog::Log(LOGINFO, "create actors table");
163 m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
165 CLog::Log(LOGINFO, "create path table");
166 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)");
167 m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
169 CLog::Log(LOGINFO, "create files table");
170 m_pDS->exec("CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text, dateAdded text)");
171 m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
173 CLog::Log(LOGINFO, "create tvshow table");
174 columns = "CREATE TABLE tvshow ( idShow integer primary key";
175 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
178 column.Format(",c%02d text", i);
182 m_pDS->exec(columns.c_str());
184 CLog::Log(LOGINFO, "create directorlinktvshow table");
185 m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
186 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
187 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
189 CLog::Log(LOGINFO, "create actorlinktvshow table");
190 m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text, iOrder integer)\n");
191 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
192 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
194 CLog::Log(LOGINFO, "create studiolinktvshow table");
195 m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
196 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
197 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
199 CLog::Log(LOGINFO, "create episode table");
200 columns = "CREATE TABLE episode ( idEpisode integer primary key, idFile integer";
201 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
204 if ( i == VIDEODB_ID_EPISODE_SEASON || i == VIDEODB_ID_EPISODE_EPISODE || i == VIDEODB_ID_EPISODE_BOOKMARK)
205 column.Format(",c%02d varchar(24)", i);
207 column.Format(",c%02d text", i);
211 columns += ", idShow integer)";
212 m_pDS->exec(columns.c_str());
213 m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
214 m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
215 CStdString createColIndex;
216 createColIndex.Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
217 m_pDS->exec(createColIndex.c_str());
218 createColIndex.Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
219 m_pDS->exec(createColIndex.c_str());
220 m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
221 m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
223 CLog::Log(LOGINFO, "create tvshowlinkpath table");
224 m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
225 m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
226 m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
228 CLog::Log(LOGINFO, "create actorlinkepisode table");
229 m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text, iOrder integer)\n");
230 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
231 m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
233 CLog::Log(LOGINFO, "create directorlinkepisode table");
234 m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
235 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
236 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
238 CLog::Log(LOGINFO, "create writerlinkepisode table");
239 m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
240 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
241 m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
243 CLog::Log(LOGINFO, "create genrelinktvshow table");
244 m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
245 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
246 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
248 CLog::Log(LOGINFO, "create movielinktvshow table");
249 m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
250 m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
251 m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
253 CLog::Log(LOGINFO, "create studio table");
254 m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
256 CLog::Log(LOGINFO, "create studiolinkmovie table");
257 m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
258 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
259 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
261 CLog::Log(LOGINFO, "create musicvideo table");
262 columns = "CREATE TABLE musicvideo ( idMVideo integer primary key, idFile integer";
263 for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
266 column.Format(",c%02d text", i);
270 m_pDS->exec(columns.c_str());
272 m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
273 m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
275 CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
276 m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
277 m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
278 m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
280 CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
281 m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
282 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
283 m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
285 CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
286 m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
287 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
288 m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
290 CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
291 m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
292 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
293 m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
295 CLog::Log(LOGINFO, "create streaminfo table");
296 m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
297 "strVideoCodec text, fVideoAspect float, iVideoWidth integer, iVideoHeight integer, "
298 "strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text, iVideoDuration integer)");
299 m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
301 CLog::Log(LOGINFO, "create sets table");
302 m_pDS->exec("CREATE TABLE sets ( idSet integer primary key, strSet text)\n");
304 // create basepath indices
305 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
306 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
307 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
308 m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c17(12) )");
310 CLog::Log(LOGINFO, "create seasons table");
311 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
312 m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
314 CLog::Log(LOGINFO, "create art table");
315 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
316 m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
318 CLog::Log(LOGINFO, "create tag table");
319 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
320 m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
322 CLog::Log(LOGINFO, "create taglinks table");
323 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
324 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
325 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
326 m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
328 CLog::Log(LOGINFO, "create deletion triggers");
329 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
330 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
331 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
333 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
334 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
335 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
337 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
338 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
339 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
341 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
342 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
344 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
345 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
347 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
348 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
350 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
351 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
353 m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
354 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
357 // we create views last to ensure all indexes are rolled in
362 CLog::Log(LOGERROR, "%s unable to create tables:%i", __FUNCTION__, (int)GetLastError());
363 RollbackTransaction();
370 void CVideoDatabase::CreateViews()
372 CLog::Log(LOGINFO, "create episodeview");
373 m_pDS->exec("DROP VIEW IF EXISTS episodeview");
374 CStdString episodeview = PrepareSQL("CREATE VIEW episodeview AS SELECT "
376 " files.strFileName AS strFileName,"
377 " path.strPath AS strPath,"
378 " files.playCount AS playCount,"
379 " files.lastPlayed AS lastPlayed,"
380 " files.dateAdded AS dateAdded,"
381 " tvshow.c%02d AS strTitle,"
382 " tvshow.c%02d AS strStudio,"
383 " tvshow.c%02d AS premiered,"
384 " tvshow.c%02d AS mpaa,"
385 " tvshow.c%02d AS strShowPath, "
386 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
387 " bookmark.totalTimeInSeconds AS totalTimeInSeconds, "
388 " seasons.idSeason AS idSeason "
391 " files.idFile=episode.idFile"
393 " tvshow.idShow=episode.idShow"
394 " LEFT JOIN seasons ON"
395 " seasons.idShow=episode.idShow AND seasons.season=episode.c%02d"
397 " files.idPath=path.idPath"
398 " LEFT JOIN bookmark ON"
399 " 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);
400 m_pDS->exec(episodeview.c_str());
402 CLog::Log(LOGINFO, "create tvshowview");
403 m_pDS->exec("DROP VIEW IF EXISTS tvshowview");
404 CStdString tvshowview = PrepareSQL("CREATE VIEW tvshowview AS SELECT "
406 " path.strPath AS strPath,"
407 " path.dateAdded AS dateAdded,"
408 " MAX(files.lastPlayed) AS lastPlayed,"
409 " NULLIF(COUNT(episode.c12), 0) AS totalCount,"
410 " COUNT(files.playCount) AS watchedcount,"
411 " NULLIF(COUNT(DISTINCT(episode.c12)), 0) AS totalSeasons "
413 " LEFT JOIN tvshowlinkpath ON"
414 " tvshowlinkpath.idShow=tvshow.idShow"
416 " path.idPath=tvshowlinkpath.idPath"
417 " LEFT JOIN episode ON"
418 " episode.idShow=tvshow.idShow"
419 " LEFT JOIN files ON"
420 " files.idFile=episode.idFile "
421 "GROUP BY tvshow.idShow;");
422 m_pDS->exec(tvshowview.c_str());
424 CLog::Log(LOGINFO, "create musicvideoview");
425 m_pDS->exec("DROP VIEW IF EXISTS musicvideoview");
426 m_pDS->exec("CREATE VIEW musicvideoview AS SELECT"
428 " files.strFileName as strFileName,"
429 " path.strPath as strPath,"
430 " files.playCount as playCount,"
431 " files.lastPlayed as lastPlayed,"
432 " files.dateAdded as dateAdded, "
433 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
434 " bookmark.totalTimeInSeconds AS totalTimeInSeconds "
437 " files.idFile=musicvideo.idFile"
439 " path.idPath=files.idPath"
440 " LEFT JOIN bookmark ON"
441 " bookmark.idFile=musicvideo.idFile AND bookmark.type=1");
443 CLog::Log(LOGINFO, "create movieview");
444 m_pDS->exec("DROP VIEW IF EXISTS movieview");
445 m_pDS->exec("CREATE VIEW movieview AS SELECT"
447 " sets.strSet AS strSet,"
448 " files.strFileName AS strFileName,"
449 " path.strPath AS strPath,"
450 " files.playCount AS playCount,"
451 " files.lastPlayed AS lastPlayed, "
452 " files.dateAdded AS dateAdded, "
453 " bookmark.timeInSeconds AS resumeTimeInSeconds, "
454 " bookmark.totalTimeInSeconds AS totalTimeInSeconds "
457 " sets.idSet = movie.idSet"
459 " files.idFile=movie.idFile"
461 " path.idPath=files.idPath"
462 " LEFT JOIN bookmark ON"
463 " bookmark.idFile=movie.idFile AND bookmark.type=1");
466 //********************************************************************************************************************************
467 int CVideoDatabase::GetPathId(const CStdString& strPath)
473 if (NULL == m_pDB.get()) return -1;
474 if (NULL == m_pDS.get()) return -1;
476 CStdString strPath1(strPath);
477 if (URIUtils::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
478 URIUtils::GetParentPath(strPath,strPath1);
480 URIUtils::AddSlashAtEnd(strPath1);
482 strSQL=PrepareSQL("select idPath from path where strPath='%s'",strPath1.c_str());
483 m_pDS->query(strSQL.c_str());
485 idPath = m_pDS->fv("path.idPath").get_asInt();
492 CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
497 bool CVideoDatabase::GetPaths(set<CStdString> &paths)
501 if (NULL == m_pDB.get()) return false;
502 if (NULL == m_pDS.get()) return false;
506 // grab all paths with movie content set
507 if (!m_pDS->query("select strPath,noUpdate from path"
508 " where (strContent = 'movies' or strContent = 'musicvideos')"
509 " and strPath NOT like 'multipath://%%'"
510 " order by strPath"))
513 while (!m_pDS->eof())
515 if (!m_pDS->fv("noUpdate").get_asBool())
516 paths.insert(m_pDS->fv("strPath").get_asString());
521 // then grab all tvshow paths
522 if (!m_pDS->query("select strPath,noUpdate from path"
523 " where ( strContent = 'tvshows'"
524 " or idPath in (select idPath from tvshowlinkpath))"
525 " and strPath NOT like 'multipath://%%'"
526 " order by strPath"))
529 while (!m_pDS->eof())
531 if (!m_pDS->fv("noUpdate").get_asBool())
532 paths.insert(m_pDS->fv("strPath").get_asString());
537 // finally grab all other paths holding a movie which is not a stack or a rar archive
538 // - this isnt perfect but it should do fine in most situations.
539 // reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
540 // not making mistakes must take priority
541 if (!m_pDS->query("select strPath,noUpdate from path"
542 " where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
543 " and idPath NOT in (select idPath from tvshowlinkpath)"
544 " 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
545 " and idPath NOT in (select idPath from files where strFileName like 'index.bdmv')" // bluray folders get stacked to a single item in parent folder
546 " and strPath NOT like 'multipath://%%'"
547 " and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
548 " order by strPath"))
551 while (!m_pDS->eof())
553 if (!m_pDS->fv("noUpdate").get_asBool())
554 paths.insert(m_pDS->fv("strPath").get_asString());
562 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
567 bool CVideoDatabase::GetPathsForTvShow(int idShow, set<int>& paths)
572 if (NULL == m_pDB.get()) return false;
573 if (NULL == m_pDS.get()) return false;
574 strSQL = PrepareSQL("SELECT DISTINCT idPath FROM files JOIN episode ON episode.idFile=files.idFile WHERE episode.idShow=%i",idShow);
575 m_pDS->query(strSQL.c_str());
576 while (!m_pDS->eof())
578 paths.insert(m_pDS->fv(0).get_asInt());
586 CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
591 int CVideoDatabase::RunQuery(const CStdString &sql)
593 unsigned int time = XbmcThreads::SystemClockMillis();
595 if (m_pDS->query(sql.c_str()))
597 rows = m_pDS->num_rows();
601 CLog::Log(LOGDEBUG, "%s took %d ms for %d items query: %s", __FUNCTION__, XbmcThreads::SystemClockMillis() - time, rows, sql.c_str());
605 bool CVideoDatabase::GetSubPaths(const CStdString &basepath, vector< pair<int,string> >& subpaths)
610 if (!m_pDB.get() || !m_pDS.get())
613 CStdString path(basepath);
614 URIUtils::AddSlashAtEnd(path);
615 sql = PrepareSQL("SELECT idPath,strPath FROM path WHERE SUBSTR(strPath,1,%i)='%s'", StringUtils::utf8_strlen(path.c_str()), path.c_str());
616 m_pDS->query(sql.c_str());
617 while (!m_pDS->eof())
619 subpaths.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
627 CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, sql.c_str());
632 int CVideoDatabase::AddPath(const CStdString& strPath, const CStdString &strDateAdded /*= "" */)
637 int idPath = GetPathId(strPath);
639 return idPath; // already have the path
641 if (NULL == m_pDB.get()) return -1;
642 if (NULL == m_pDS.get()) return -1;
644 CStdString strPath1(strPath);
645 if (URIUtils::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
646 URIUtils::GetParentPath(strPath,strPath1);
648 URIUtils::AddSlashAtEnd(strPath1);
650 // only set dateadded if we got one
651 if (!strDateAdded.empty())
652 strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper, dateAdded) values (NULL,'%s','','', '%s')", strPath1.c_str(), strDateAdded.c_str());
654 strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
655 m_pDS->exec(strSQL.c_str());
656 idPath = (int)m_pDS->lastinsertid();
661 CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
666 bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
670 if (NULL == m_pDB.get()) return false;
671 if (NULL == m_pDS.get()) return false;
673 CStdString strSQL=PrepareSQL("select strHash from path where strPath='%s'", path.c_str());
674 m_pDS->query(strSQL.c_str());
675 if (m_pDS->num_rows() == 0)
677 hash = m_pDS->fv("strHash").get_asString();
682 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
688 //********************************************************************************************************************************
689 int CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
691 CStdString strSQL = "";
695 if (NULL == m_pDB.get()) return -1;
696 if (NULL == m_pDS.get()) return -1;
698 CStdString strFileName, strPath;
699 SplitPath(strFileNameAndPath,strPath,strFileName);
701 int idPath = AddPath(strPath);
705 CStdString strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
707 m_pDS->query(strSQL.c_str());
708 if (m_pDS->num_rows() > 0)
710 idFile = m_pDS->fv("idFile").get_asInt() ;
716 strSQL=PrepareSQL("insert into files (idFile, idPath, strFileName) values(NULL, %i, '%s')", idPath, strFileName.c_str());
717 m_pDS->exec(strSQL.c_str());
718 idFile = (int)m_pDS->lastinsertid();
723 CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
728 int CVideoDatabase::AddFile(const CFileItem& item)
730 if (item.IsVideoDb() && item.HasVideoInfoTag())
731 return AddFile(item.GetVideoInfoTag()->m_strFileNameAndPath);
732 return AddFile(item.GetPath());
735 void CVideoDatabase::UpdateFileDateAdded(int idFile, const CStdString& strFileNameAndPath)
737 if (idFile < 0 || strFileNameAndPath.empty())
740 CStdString strSQL = "";
743 if (NULL == m_pDB.get()) return;
744 if (NULL == m_pDS.get()) return;
746 CStdString file = strFileNameAndPath;
747 if (URIUtils::IsStack(strFileNameAndPath))
748 file = CStackDirectory::GetFirstStackedFile(strFileNameAndPath);
750 if (URIUtils::IsInArchive(file))
751 file = CURL(file).GetHostName();
754 // Skip looking at the files ctime/mtime if defined by the user through as.xml
755 if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
757 // Let's try to get the modification datetime
758 struct __stat64 buffer;
759 if (CFile::Stat(file, &buffer) == 0 && (buffer.st_mtime != 0 || buffer.st_ctime !=0))
761 time_t now = time(NULL);
763 // Prefer the modification time if it's valid
764 if (g_advancedSettings.m_iVideoLibraryDateAdded == 1)
766 if (buffer.st_mtime != 0 && (time_t)buffer.st_mtime <= now)
767 addedTime = (time_t)buffer.st_mtime;
769 addedTime = (time_t)buffer.st_ctime;
771 // Use the newer of the creation and modification time
774 addedTime = max((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
775 // if the newer of the two dates is in the future, we try it with the older one
777 addedTime = min((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
780 // make sure the datetime does is not in the future
781 if (addedTime <= now)
783 struct tm *time = localtime(&addedTime);
790 if (!dateAdded.IsValid())
791 dateAdded = CDateTime::GetCurrentDateTime();
793 strSQL = PrepareSQL("update files set dateAdded='%s' where idFile=%d", dateAdded.GetAsDBDateTime().c_str(), idFile);
794 m_pDS->exec(strSQL.c_str());
798 CLog::Log(LOGERROR, "%s unable to update dateadded for file (%s)", __FUNCTION__, strSQL.c_str());
802 bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
806 if (NULL == m_pDB.get()) return false;
807 if (NULL == m_pDS.get()) return false;
810 { // this is an empty folder - we need only add it to the path table
811 // if the path actually exists
812 if (!CDirectory::Exists(path))
815 int idPath = AddPath(path);
816 if (idPath < 0) return false;
818 CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
819 m_pDS->exec(strSQL.c_str());
825 CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
831 bool CVideoDatabase::LinkMovieToTvshow(int idMovie, int idShow, bool bRemove)
835 if (NULL == m_pDB.get()) return false;
836 if (NULL == m_pDS.get()) return false;
838 if (bRemove) // delete link
840 CStdString strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i and idShow=%i", idMovie, idShow);
841 m_pDS->exec(strSQL.c_str());
845 CStdString strSQL=PrepareSQL("insert into movielinktvshow (idShow,idMovie) values (%i,%i)", idShow,idMovie);
846 m_pDS->exec(strSQL.c_str());
852 CLog::Log(LOGERROR, "%s (%i, %i) failed", __FUNCTION__, idMovie, idShow);
858 bool CVideoDatabase::IsLinkedToTvshow(int idMovie)
862 if (NULL == m_pDB.get()) return false;
863 if (NULL == m_pDS.get()) return false;
865 CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
866 m_pDS->query(strSQL.c_str());
878 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
884 bool CVideoDatabase::GetLinksToTvShow(int idMovie, vector<int>& ids)
888 if (NULL == m_pDB.get()) return false;
889 if (NULL == m_pDS.get()) return false;
891 CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
892 m_pDS2->query(strSQL.c_str());
893 while (!m_pDS2->eof())
895 ids.push_back(m_pDS2->fv(1).get_asInt());
904 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
911 //********************************************************************************************************************************
912 int CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
916 if (NULL == m_pDB.get()) return -1;
917 if (NULL == m_pDS.get()) return -1;
918 CStdString strPath, strFileName;
919 SplitPath(strFilenameAndPath,strPath,strFileName);
921 int idPath = GetPathId(strPath);
925 strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
926 m_pDS->query(strSQL.c_str());
927 if (m_pDS->num_rows() > 0)
929 int idFile = m_pDS->fv("files.idFile").get_asInt();
937 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
942 int CVideoDatabase::GetFileId(const CFileItem &item)
944 if (item.IsVideoDb() && item.HasVideoInfoTag())
945 return GetFileId(item.GetVideoInfoTag()->m_strFileNameAndPath);
946 return GetFileId(item.GetPath());
949 //********************************************************************************************************************************
950 int CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
954 if (NULL == m_pDB.get()) return -1;
955 if (NULL == m_pDS.get()) return -1;
958 // needed for query parameters
959 int idFile = GetFileId(strFilenameAndPath);
965 SplitPath(strFilenameAndPath,strPath,strFile);
967 // have to join movieinfo table for correct results
968 idPath = GetPathId(strPath);
969 if (idPath < 0 && strPath != strFilenameAndPath)
973 if (idFile == -1 && strPath != strFilenameAndPath)
978 strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
980 strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
982 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
983 m_pDS->query(strSQL.c_str());
984 if (m_pDS->num_rows() > 0)
985 idMovie = m_pDS->fv("idMovie").get_asInt();
992 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
997 int CVideoDatabase::GetTvShowId(const CStdString& strPath)
1001 if (NULL == m_pDB.get()) return -1;
1002 if (NULL == m_pDS.get()) return -1;
1005 // have to join movieinfo table for correct results
1006 int idPath = GetPathId(strPath);
1011 CStdString strPath1=strPath;
1012 CStdString strParent;
1015 strSQL=PrepareSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%i",idPath);
1016 m_pDS->query(strSQL);
1020 while (iFound == 0 && URIUtils::GetParentPath(strPath1, strParent))
1022 strSQL=PrepareSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and strPath='%s'",strParent.c_str());
1023 m_pDS->query(strSQL.c_str());
1026 int idShow = m_pDS->fv("idShow").get_asInt();
1030 strPath1 = strParent;
1033 if (m_pDS->num_rows() > 0)
1034 idTvShow = m_pDS->fv("idShow").get_asInt();
1041 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1046 int CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, int idEpisode, int idSeason) // input value is episode/season number hint - for multiparters
1050 if (NULL == m_pDB.get()) return -1;
1051 if (NULL == m_pDS.get()) return -1;
1053 // need this due to the nested GetEpisodeInfo query
1054 auto_ptr<Dataset> pDS;
1055 pDS.reset(m_pDB->CreateDataset());
1056 if (NULL == pDS.get()) return -1;
1058 int idFile = GetFileId(strFilenameAndPath);
1062 CStdString strSQL=PrepareSQL("select idEpisode from episode where idFile=%i", idFile);
1064 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
1065 pDS->query(strSQL.c_str());
1066 if (pDS->num_rows() > 0)
1068 if (idEpisode == -1)
1069 idEpisode = pDS->fv("episode.idEpisode").get_asInt();
1070 else // use the hint!
1075 int idTmpEpisode = pDS->fv("episode.idEpisode").get_asInt();
1076 GetEpisodeInfo(strFilenameAndPath,tag,idTmpEpisode);
1077 if (tag.m_iEpisode == idEpisode && (idSeason == -1 || tag.m_iSeason == idSeason)) {
1078 // match on the episode hint, and there's no season hint or a season hint match
1079 idEpisode = idTmpEpisode;
1097 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1102 int CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
1106 if (NULL == m_pDB.get()) return -1;
1107 if (NULL == m_pDS.get()) return -1;
1109 int idFile = GetFileId(strFilenameAndPath);
1113 CStdString strSQL=PrepareSQL("select idMVideo from musicvideo where idFile=%i", idFile);
1115 CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
1116 m_pDS->query(strSQL.c_str());
1118 if (m_pDS->num_rows() > 0)
1119 idMVideo = m_pDS->fv("idMVideo").get_asInt();
1126 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1131 //********************************************************************************************************************************
1132 int CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
1136 if (NULL == m_pDB.get()) return -1;
1137 if (NULL == m_pDS.get()) return -1;
1139 int idMovie = GetMovieId(strFilenameAndPath);
1142 int idFile = AddFile(strFilenameAndPath);
1145 UpdateFileDateAdded(idFile, strFilenameAndPath);
1146 CStdString strSQL=PrepareSQL("insert into movie (idMovie, idFile) values (NULL, %i)", idFile);
1147 m_pDS->exec(strSQL.c_str());
1148 idMovie = (int)m_pDS->lastinsertid();
1149 // CommitTransaction();
1156 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1161 int CVideoDatabase::AddTvShow(const CStdString& strPath)
1165 if (NULL == m_pDB.get()) return -1;
1166 if (NULL == m_pDS.get()) return -1;
1168 CStdString strSQL=PrepareSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath='%s' and path.idPath=tvshowlinkpath.idPath",strPath.c_str());
1169 m_pDS->query(strSQL.c_str());
1170 if (m_pDS->num_rows() != 0)
1171 return m_pDS->fv("tvshowlinkpath.idShow").get_asInt();
1173 strSQL=PrepareSQL("insert into tvshow (idShow) values (NULL)");
1174 m_pDS->exec(strSQL.c_str());
1175 int idTvShow = (int)m_pDS->lastinsertid();
1177 // Get the creation datetime of the tvshow directory
1178 CDateTime dateAdded;
1179 // Skip looking at the files ctime/mtime if defined by the user through as.xml
1180 if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
1182 struct __stat64 buffer;
1183 if (XFILE::CFile::Stat(strPath, &buffer) == 0)
1185 time_t now = time(NULL);
1186 // Make sure we have a valid date (i.e. not in the future)
1187 if ((time_t)buffer.st_ctime <= now)
1189 struct tm *time = localtime((const time_t*)&buffer.st_ctime);
1196 if (!dateAdded.IsValid())
1197 dateAdded = CDateTime::GetCurrentDateTime();
1199 int idPath = AddPath(strPath, dateAdded.GetAsDBDateTime());
1200 strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
1201 m_pDS->exec(strSQL.c_str());
1203 // CommitTransaction();
1209 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1214 //********************************************************************************************************************************
1215 int CVideoDatabase::AddEpisode(int idShow, const CStdString& strFilenameAndPath)
1219 if (NULL == m_pDB.get()) return -1;
1220 if (NULL == m_pDS.get()) return -1;
1222 int idFile = AddFile(strFilenameAndPath);
1225 UpdateFileDateAdded(idFile, strFilenameAndPath);
1227 CStdString strSQL=PrepareSQL("insert into episode (idEpisode, idFile, idShow) values (NULL, %i, %i)", idFile, idShow);
1228 m_pDS->exec(strSQL.c_str());
1229 return (int)m_pDS->lastinsertid();
1233 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1238 int CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
1242 if (NULL == m_pDB.get()) return -1;
1243 if (NULL == m_pDS.get()) return -1;
1245 int idMVideo = GetMusicVideoId(strFilenameAndPath);
1248 int idFile = AddFile(strFilenameAndPath);
1251 UpdateFileDateAdded(idFile, strFilenameAndPath);
1252 CStdString strSQL=PrepareSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %i)", idFile);
1253 m_pDS->exec(strSQL.c_str());
1254 idMVideo = (int)m_pDS->lastinsertid();
1261 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1266 //********************************************************************************************************************************
1267 int CVideoDatabase::AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value)
1271 if (NULL == m_pDB.get()) return -1;
1272 if (NULL == m_pDS.get()) return -1;
1274 CStdString strSQL = PrepareSQL("select %s from %s where %s like '%s'", firstField.c_str(), table.c_str(), secondField.c_str(), value.c_str());
1275 m_pDS->query(strSQL.c_str());
1276 if (m_pDS->num_rows() == 0)
1279 // doesnt exists, add it
1280 strSQL = PrepareSQL("insert into %s (%s, %s) values(NULL, '%s')", table.c_str(), firstField.c_str(), secondField.c_str(), value.c_str());
1281 m_pDS->exec(strSQL.c_str());
1282 int id = (int)m_pDS->lastinsertid();
1287 int id = m_pDS->fv(firstField).get_asInt();
1294 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, value.c_str() );
1300 int CVideoDatabase::AddSet(const CStdString& strSet)
1305 return AddToTable("sets", "idSet", "strSet", strSet);
1308 int CVideoDatabase::AddTag(const std::string& tag)
1313 return AddToTable("tag", "idTag", "strTag", tag);
1316 int CVideoDatabase::AddGenre(const CStdString& strGenre)
1318 return AddToTable("genre", "idGenre", "strGenre", strGenre);
1321 int CVideoDatabase::AddStudio(const CStdString& strStudio)
1323 return AddToTable("studio", "idStudio", "strStudio", strStudio);
1326 //********************************************************************************************************************************
1327 int CVideoDatabase::AddCountry(const CStdString& strCountry)
1329 return AddToTable("country", "idCountry", "strCountry", strCountry);
1332 int CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& thumbURLs, const CStdString &thumb)
1336 if (NULL == m_pDB.get()) return -1;
1337 if (NULL == m_pDS.get()) return -1;
1339 CStdString strSQL=PrepareSQL("select idActor from actors where strActor like '%s'", strActor.c_str());
1340 m_pDS->query(strSQL.c_str());
1341 if (m_pDS->num_rows() == 0)
1344 // doesnt exists, add it
1345 strSQL=PrepareSQL("insert into actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),thumbURLs.c_str());
1346 m_pDS->exec(strSQL.c_str());
1347 idActor = (int)m_pDS->lastinsertid();
1351 idActor = m_pDS->fv("idActor").get_asInt();
1353 // update the thumb url's
1354 if (!thumbURLs.IsEmpty())
1356 strSQL=PrepareSQL("update actors set strThumb='%s' where idActor=%i",thumbURLs.c_str(),idActor);
1357 m_pDS->exec(strSQL.c_str());
1361 if (!thumb.IsEmpty())
1362 SetArtForItem(idActor, "actor", "thumb", thumb);
1367 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
1374 void CVideoDatabase::AddLinkToActor(const char *table, int actorID, const char *secondField, int secondID, const CStdString &role, int order)
1378 if (NULL == m_pDB.get()) return ;
1379 if (NULL == m_pDS.get()) return ;
1381 CStdString strSQL=PrepareSQL("select * from %s where idActor=%i and %s=%i", table, actorID, secondField, secondID);
1382 m_pDS->query(strSQL.c_str());
1383 if (m_pDS->num_rows() == 0)
1385 // doesnt exists, add it
1386 strSQL=PrepareSQL("insert into %s (idActor, %s, strRole, iOrder) values(%i,%i,'%s',%i)", table, secondField, actorID, secondID, role.c_str(), order);
1387 m_pDS->exec(strSQL.c_str());
1393 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1397 void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1401 if (NULL == m_pDB.get()) return ;
1402 if (NULL == m_pDS.get()) return ;
1404 CStdString strSQL = PrepareSQL("select * from %s where %s=%i and %s=%i", table, firstField, firstID, secondField, secondID);
1405 if (typeField != NULL && type != NULL)
1406 strSQL += PrepareSQL(" and %s='%s'", typeField, type);
1407 m_pDS->query(strSQL.c_str());
1408 if (m_pDS->num_rows() == 0)
1410 // doesnt exists, add it
1411 if (typeField == NULL || type == NULL)
1412 strSQL = PrepareSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
1414 strSQL = PrepareSQL("insert into %s (%s,%s,%s) values(%i,%i,'%s')", table, firstField, secondField, typeField, firstID, secondID, type);
1415 m_pDS->exec(strSQL.c_str());
1421 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1425 void CVideoDatabase::RemoveFromLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID, const char *typeField /* = NULL */, const char *type /* = NULL */)
1429 if (NULL == m_pDB.get()) return ;
1430 if (NULL == m_pDS.get()) return ;
1432 CStdString strSQL = PrepareSQL("DELETE FROM %s WHERE %s = %i AND %s = %i", table, firstField, firstID, secondField, secondID);
1433 if (typeField != NULL && type != NULL)
1434 strSQL += PrepareSQL(" AND %s='%s'", typeField, type);
1435 m_pDS->exec(strSQL.c_str());
1439 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
1444 void CVideoDatabase::AddTagToItem(int idMovie, int idTag, const std::string &type)
1449 AddToLinkTable("taglinks", "idTag", idTag, "idMedia", idMovie, "media_type", type.c_str());
1452 void CVideoDatabase::RemoveTagFromItem(int idItem, int idTag, const std::string &type)
1457 RemoveFromLinkTable("taglinks", "idTag", idTag, "idMedia", idItem, "media_type", type.c_str());
1460 void CVideoDatabase::RemoveTagsFromItem(int idItem, const std::string &type)
1465 m_pDS2->exec(PrepareSQL("DELETE FROM taglinks WHERE idMedia=%d AND media_type='%s'", idItem, type.c_str()));
1469 void CVideoDatabase::AddActorToMovie(int idMovie, int idActor, const CStdString& strRole, int order)
1471 AddLinkToActor("actorlinkmovie", idActor, "idMovie", idMovie, strRole, order);
1474 void CVideoDatabase::AddActorToTvShow(int idTvShow, int idActor, const CStdString& strRole, int order)
1476 AddLinkToActor("actorlinktvshow", idActor, "idShow", idTvShow, strRole, order);
1479 void CVideoDatabase::AddActorToEpisode(int idEpisode, int idActor, const CStdString& strRole, int order)
1481 AddLinkToActor("actorlinkepisode", idActor, "idEpisode", idEpisode, strRole, order);
1484 void CVideoDatabase::AddArtistToMusicVideo(int idMVideo, int idArtist)
1486 AddToLinkTable("artistlinkmusicvideo", "idArtist", idArtist, "idMVideo", idMVideo);
1489 //****Directors + Writers****
1490 void CVideoDatabase::AddDirectorToMovie(int idMovie, int idDirector)
1492 AddToLinkTable("directorlinkmovie", "idDirector", idDirector, "idMovie", idMovie);
1495 void CVideoDatabase::AddDirectorToTvShow(int idTvShow, int idDirector)
1497 AddToLinkTable("directorlinktvshow", "idDirector", idDirector, "idShow", idTvShow);
1500 void CVideoDatabase::AddWriterToEpisode(int idEpisode, int idWriter)
1502 AddToLinkTable("writerlinkepisode", "idWriter", idWriter, "idEpisode", idEpisode);
1505 void CVideoDatabase::AddWriterToMovie(int idMovie, int idWriter)
1507 AddToLinkTable("writerlinkmovie", "idWriter", idWriter, "idMovie", idMovie);
1510 void CVideoDatabase::AddDirectorToEpisode(int idEpisode, int idDirector)
1512 AddToLinkTable("directorlinkepisode", "idDirector", idDirector, "idEpisode", idEpisode);
1515 void CVideoDatabase::AddDirectorToMusicVideo(int idMVideo, int idDirector)
1517 AddToLinkTable("directorlinkmusicvideo", "idDirector", idDirector, "idMVideo", idMVideo);
1521 void CVideoDatabase::AddStudioToMovie(int idMovie, int idStudio)
1523 AddToLinkTable("studiolinkmovie", "idStudio", idStudio, "idMovie", idMovie);
1526 void CVideoDatabase::AddStudioToTvShow(int idTvShow, int idStudio)
1528 AddToLinkTable("studiolinktvshow", "idStudio", idStudio, "idShow", idTvShow);
1531 void CVideoDatabase::AddStudioToMusicVideo(int idMVideo, int idStudio)
1533 AddToLinkTable("studiolinkmusicvideo", "idStudio", idStudio, "idMVideo", idMVideo);
1537 void CVideoDatabase::AddGenreToMovie(int idMovie, int idGenre)
1539 AddToLinkTable("genrelinkmovie", "idGenre", idGenre, "idMovie", idMovie);
1542 void CVideoDatabase::AddGenreToTvShow(int idTvShow, int idGenre)
1544 AddToLinkTable("genrelinktvshow", "idGenre", idGenre, "idShow", idTvShow);
1547 void CVideoDatabase::AddGenreToMusicVideo(int idMVideo, int idGenre)
1549 AddToLinkTable("genrelinkmusicvideo", "idGenre", idGenre, "idMVideo", idMVideo);
1553 void CVideoDatabase::AddCountryToMovie(int idMovie, int idCountry)
1555 AddToLinkTable("countrylinkmovie", "idCountry", idCountry, "idMovie", idMovie);
1558 //********************************************************************************************************************************
1559 bool CVideoDatabase::LoadVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details)
1561 if (GetMovieInfo(strFilenameAndPath, details))
1563 if (GetEpisodeInfo(strFilenameAndPath, details))
1565 if (GetMusicVideoInfo(strFilenameAndPath, details))
1567 if (GetFileInfo(strFilenameAndPath, details))
1573 bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
1577 if (NULL == m_pDB.get()) return false;
1578 if (NULL == m_pDS.get()) return false;
1579 int idMovie = GetMovieId(strFilenameAndPath);
1580 return (idMovie > 0); // index of zero is also invalid
1584 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1589 bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
1593 if (NULL == m_pDB.get()) return false;
1594 if (NULL == m_pDS.get()) return false;
1595 int idTvShow = GetTvShowId(strPath);
1596 return (idTvShow > 0); // index of zero is also invalid
1600 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1605 bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
1609 if (NULL == m_pDB.get()) return false;
1610 if (NULL == m_pDS.get()) return false;
1611 int idEpisode = GetEpisodeId(strFilenameAndPath);
1612 return (idEpisode > 0); // index of zero is also invalid
1616 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1621 bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
1625 if (NULL == m_pDB.get()) return false;
1626 if (NULL == m_pDS.get()) return false;
1627 int idMVideo = GetMusicVideoId(strFilenameAndPath);
1628 return (idMVideo > 0); // index of zero is also invalid
1632 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1637 void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath, int idTvShow /* = -1 */)
1641 if (NULL == m_pDB.get()) return ;
1642 if (NULL == m_pDS.get()) return ;
1646 idTvShow = GetTvShowId(strPath);
1652 strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
1653 m_pDS->exec(strSQL.c_str());
1655 strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
1656 m_pDS->exec(strSQL.c_str());
1658 strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
1659 m_pDS->exec(strSQL.c_str());
1661 strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
1662 m_pDS->exec(strSQL.c_str());
1664 // remove all info other than the id
1665 // we do this due to the way we have the link between the file + movie tables.
1667 strSQL = "update tvshow set ";
1668 for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
1671 column.Format("c%02d=NULL,", iType);
1674 strSQL = strSQL.Mid(0, strSQL.size() - 1) + PrepareSQL(" where idShow=%i", idTvShow);
1675 m_pDS->exec(strSQL.c_str());
1679 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1683 //********************************************************************************************************************************
1684 void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
1687 filter.join = "LEFT JOIN actorlinkmovie ON actorlinkmovie.idMovie=movieview.idMovie "
1688 "LEFT JOIN actors a ON a.idActor=actorlinkmovie.idActor "
1689 "LEFT JOIN directorlinkmovie ON directorlinkmovie.idMovie=movieview.idMovie "
1690 "LEFT JOIN actors d ON d.idActor=directorlinkmovie.idDirector";
1691 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1692 filter.group = "movieview.idMovie";
1693 GetMoviesByWhere("videodb://movies/titles/", filter, items);
1696 void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
1699 filter.join = "LEFT JOIN actorlinktvshow ON actorlinktvshow.idShow=tvshowview.idShow "
1700 "LEFT JOIN actors a ON a.idActor=actorlinktvshow.idActor "
1701 "LEFT JOIN directorlinktvshow ON directorlinktvshow.idShow=tvshowview.idShow "
1702 "LEFT JOIN actors d ON d.idActor=directorlinktvshow.idDirector";
1703 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1704 filter.group = "tvshowview.idShow";
1705 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
1708 void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
1711 filter.join = "LEFT JOIN actorlinkepisode ON actorlinkepisode.idEpisode=episodeview.idEpisode "
1712 "LEFT JOIN actors a ON a.idActor=actorlinkepisode.idActor "
1713 "LEFT JOIN directorlinkepisode ON directorlinkepisode.idEpisode=episodeview.idEpisode "
1714 "LEFT JOIN actors d ON d.idActor=directorlinkepisode.idDirector";
1715 filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
1716 filter.group = "episodeview.idEpisode";
1717 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
1720 void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
1725 if (NULL == m_pDB.get()) return ;
1726 if (NULL == m_pDS.get()) return ;
1729 if (strArtist.IsEmpty()) // TODO: SMARTPLAYLISTS what is this here for???
1730 strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
1732 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());
1733 m_pDS->query( strSQL.c_str() );
1735 while (!m_pDS->eof())
1737 CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
1738 CFileItemPtr pItem(new CFileItem(tag));
1739 pItem->SetLabel(StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator));
1747 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
1751 //********************************************************************************************************************************
1752 bool CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
1756 // TODO: Optimize this - no need for all the queries!
1758 idMovie = GetMovieId(strFilenameAndPath);
1759 if (idMovie < 0) return false;
1761 CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
1762 if (!m_pDS->query(sql.c_str()))
1764 details = GetDetailsForMovie(m_pDS, true);
1765 return !details.IsEmpty();
1769 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1774 //********************************************************************************************************************************
1775 bool CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
1780 idTvShow = GetTvShowId(strPath);
1781 if (idTvShow < 0) return false;
1783 CStdString sql = PrepareSQL("SELECT * FROM tvshowview WHERE idShow=%i", idTvShow);
1784 if (!m_pDS->query(sql.c_str()))
1786 details = GetDetailsForTvShow(m_pDS, true);
1787 return !details.IsEmpty();
1791 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
1796 bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
1800 // TODO: Optimize this - no need for all the queries!
1802 idEpisode = GetEpisodeId(strFilenameAndPath);
1803 if (idEpisode < 0) return false;
1805 CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
1806 if (!m_pDS->query(sql.c_str()))
1808 details = GetDetailsForEpisode(m_pDS, true);
1809 return !details.IsEmpty();
1813 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1818 bool CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
1822 // TODO: Optimize this - no need for all the queries!
1824 idMVideo = GetMusicVideoId(strFilenameAndPath);
1825 if (idMVideo < 0) return false;
1827 CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
1828 if (!m_pDS->query(sql.c_str()))
1830 details = GetDetailsForMusicVideo(m_pDS, true);
1831 return !details.IsEmpty();
1835 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1840 bool CVideoDatabase::GetSetInfo(int idSet, CVideoInfoTag& details)
1848 filter.where = PrepareSQL("sets.idSet=%d", idSet);
1849 CFileItemList items;
1850 if (!GetSetsByWhere("videodb://movies/sets/", filter, items) ||
1851 items.Size() != 1 ||
1852 !items[0]->HasVideoInfoTag())
1855 details = *(items[0]->GetVideoInfoTag());
1856 return !details.IsEmpty();
1860 CLog::Log(LOGERROR, "%s (%d) failed", __FUNCTION__, idSet);
1865 bool CVideoDatabase::GetFileInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idFile /* = -1 */)
1870 idFile = GetFileId(strFilenameAndPath);
1874 CStdString sql = PrepareSQL("SELECT * FROM files "
1875 "JOIN path ON path.idPath = files.idPath "
1876 "LEFT JOIN bookmark ON bookmark.idFile = files.idFile AND bookmark.type = %i "
1877 "WHERE files.idFile = %i", CBookmark::RESUME, idFile);
1878 if (!m_pDS->query(sql.c_str()))
1881 details.m_iFileId = m_pDS->fv("files.idFile").get_asInt();
1882 details.m_strPath = m_pDS->fv("path.strPath").get_asString();
1883 CStdString strFileName = m_pDS->fv("files.strFilename").get_asString();
1884 ConstructPath(details.m_strFileNameAndPath, details.m_strPath, strFileName);
1885 details.m_playCount = max(details.m_playCount, m_pDS->fv("files.playCount").get_asInt());
1886 if (!details.m_lastPlayed.IsValid())
1887 details.m_lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
1888 if (!details.m_dateAdded.IsValid())
1889 details.m_dateAdded.SetFromDBDateTime(m_pDS->fv("files.dateAdded").get_asString());
1890 if (!details.m_resumePoint.IsSet())
1892 details.m_resumePoint.timeInSeconds = m_pDS->fv("bookmark.timeInSeconds").get_asInt();
1893 details.m_resumePoint.totalTimeInSeconds = m_pDS->fv("bookmark.totalTimeInSeconds").get_asInt();
1894 details.m_resumePoint.type = CBookmark::RESUME;
1897 return !details.IsEmpty();
1901 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
1906 void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
1908 // add all directors
1909 for (unsigned int i = 0; i < details.m_director.size(); i++)
1910 vecDirectors.push_back(AddActor(details.m_director[i],""));
1913 for (unsigned int i = 0; i < details.m_genre.size(); i++)
1914 vecGenres.push_back(AddGenre(details.m_genre[i]));
1916 for (unsigned int i = 0; i < details.m_studio.size(); i++)
1917 vecStudios.push_back(AddStudio(details.m_studio[i]));
1920 CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
1923 for (int i = min + 1; i < max; ++i)
1925 switch (offsets[i].type)
1927 case VIDEODB_TYPE_STRING:
1928 sql += PrepareSQL("c%02d='%s',", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str());
1930 case VIDEODB_TYPE_INT:
1931 sql += PrepareSQL("c%02d='%i',", i, *(int*)(((char*)&details)+offsets[i].offset));
1933 case VIDEODB_TYPE_COUNT:
1935 int value = *(int*)(((char*)&details)+offsets[i].offset);
1937 sql += PrepareSQL("c%02d=%i,", i, value);
1939 sql += PrepareSQL("c%02d=NULL,", i);
1942 case VIDEODB_TYPE_BOOL:
1943 sql += PrepareSQL("c%02d='%s',", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false");
1945 case VIDEODB_TYPE_FLOAT:
1946 sql += PrepareSQL("c%02d='%f',", i, *(float*)(((char*)&details)+offsets[i].offset));
1948 case VIDEODB_TYPE_STRINGARRAY:
1949 sql += PrepareSQL("c%02d='%s',", i, StringUtils::Join(*((std::vector<std::string>*)(((char*)&details)+offsets[i].offset)), g_advancedSettings.m_videoItemSeparator).c_str());
1951 case VIDEODB_TYPE_DATE:
1952 sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDate().c_str());
1954 case VIDEODB_TYPE_DATETIME:
1955 sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDateTime().c_str());
1963 //********************************************************************************************************************************
1964 int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMovie /* = -1 */)
1971 idMovie = GetMovieId(strFilenameAndPath);
1974 DeleteMovie(strFilenameAndPath, true, idMovie); // true to keep the table entry
1977 // only add a new movie if we don't already have a valid idMovie
1978 // (DeleteMovie is called with bKeepId == true so the movie won't
1979 // be removed from the movie table)
1980 idMovie = AddMovie(strFilenameAndPath);
1983 CommitTransaction();
1988 vector<int> vecDirectors;
1989 vector<int> vecGenres;
1990 vector<int> vecStudios;
1991 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
1993 for (unsigned int i = 0; i < vecGenres.size(); ++i)
1994 AddGenreToMovie(idMovie, vecGenres[i]);
1996 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
1997 AddDirectorToMovie(idMovie, vecDirectors[i]);
1999 for (unsigned int i = 0; i < vecStudios.size(); ++i)
2000 AddStudioToMovie(idMovie, vecStudios[i]);
2003 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2004 AddWriterToMovie(idMovie, AddActor(details.m_writingCredits[i],""));
2008 for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
2010 int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
2011 AddActorToMovie(idMovie, idActor, it->strRole, order++);
2016 if (!details.m_strSet.empty())
2018 idSet = AddSet(details.m_strSet);
2019 // add art if not available
2020 map<string, string> setArt;
2021 if (!GetArtForItem(idSet, "set", setArt))
2022 SetArtForItem(idSet, "set", artwork);
2026 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2028 int idTag = AddTag(details.m_tags[i]);
2029 AddTagToItem(idMovie, idTag, "movie");
2033 for (unsigned int i = 0; i < details.m_country.size(); i++)
2034 AddCountryToMovie(idMovie, AddCountry(details.m_country[i]));
2036 if (details.HasStreamDetails())
2037 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2039 SetArtForItem(idMovie, "movie", artwork);
2041 // query DB for any movies matching imdbid and year
2042 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);
2043 m_pDS->query(strSQL.c_str());
2047 int playCount = m_pDS->fv("files.playCount").get_asInt();
2049 CDateTime lastPlayed;
2050 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2052 int idFile = GetFileId(strFilenameAndPath);
2054 // update with playCount and lastPlayed
2055 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2056 m_pDS->exec(strSQL.c_str());
2061 // update our movie table (we know it was added already above)
2062 // and insert the new row
2063 CStdString sql = "update movie set " + GetValueString(details, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
2065 sql += PrepareSQL(", idSet = %i", idSet);
2067 sql += ", idSet = NULL";
2068 sql += PrepareSQL(" where idMovie=%i", idMovie);
2069 m_pDS->exec(sql.c_str());
2070 CommitTransaction();
2076 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2081 int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, map<string, string> > &seasonArt, int idTvShow /*= -1 */)
2085 if (!m_pDB.get() || !m_pDS.get())
2087 CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
2094 idTvShow = GetTvShowId(strPath);
2097 DeleteDetailsForTvShow(strPath, idTvShow);
2100 idTvShow = AddTvShow(strPath);
2103 CommitTransaction();
2108 vector<int> vecDirectors;
2109 vector<int> vecGenres;
2110 vector<int> vecStudios;
2111 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2115 for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
2117 int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
2118 AddActorToTvShow(idTvShow, idActor, it->strRole, order++);
2122 for (i = 0; i < vecGenres.size(); ++i)
2123 AddGenreToTvShow(idTvShow, vecGenres[i]);
2125 for (i = 0; i < vecDirectors.size(); ++i)
2126 AddDirectorToTvShow(idTvShow, vecDirectors[i]);
2128 for (i = 0; i < vecStudios.size(); ++i)
2129 AddStudioToTvShow(idTvShow, vecStudios[i]);
2132 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2134 int idTag = AddTag(details.m_tags[i]);
2135 AddTagToItem(idTvShow, idTag, "tvshow");
2138 // add "all seasons" - the rest are added in SetDetailsForEpisode
2139 AddSeason(idTvShow, -1);
2141 SetArtForItem(idTvShow, "tvshow", artwork);
2142 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
2144 int idSeason = AddSeason(idTvShow, i->first);
2146 SetArtForItem(idSeason, "season", i->second);
2149 // and insert the new row
2150 CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
2151 sql += PrepareSQL(" where idShow=%i", idTvShow);
2152 m_pDS->exec(sql.c_str());
2154 CommitTransaction();
2160 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2166 int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idShow, int idEpisode)
2172 idEpisode = GetEpisodeId(strFilenameAndPath);
2175 DeleteEpisode(strFilenameAndPath, idEpisode, true); // true to keep the table entry
2178 // only add a new episode if we don't already have a valid idEpisode
2179 // (DeleteEpisode is called with bKeepId == true so the episode won't
2180 // be removed from the episode table)
2181 idEpisode = AddEpisode(idShow,strFilenameAndPath);
2184 CommitTransaction();
2189 vector<int> vecDirectors;
2190 vector<int> vecGenres;
2191 vector<int> vecStudios;
2192 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2196 for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
2198 int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
2199 AddActorToEpisode(idEpisode, idActor, it->strRole, order++);
2203 for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
2204 AddWriterToEpisode(idEpisode, AddActor(details.m_writingCredits[i],""));
2206 for (unsigned int i = 0; i < vecDirectors.size(); ++i)
2208 AddDirectorToEpisode(idEpisode, vecDirectors[i]);
2211 if (details.HasStreamDetails())
2213 if (details.m_iFileId != -1)
2214 SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
2216 SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
2219 // ensure we have this season already added
2220 AddSeason(idShow, details.m_iSeason);
2222 SetArtForItem(idEpisode, "episode", artwork);
2224 // query DB for any episodes matching idShow, Season and Episode
2225 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);
2226 m_pDS->query(strSQL.c_str());
2230 int playCount = m_pDS->fv("files.playCount").get_asInt();
2232 CDateTime lastPlayed;
2233 lastPlayed.SetFromDBDateTime(m_pDS->fv("files.lastPlayed").get_asString());
2235 int idFile = GetFileId(strFilenameAndPath);
2237 // update with playCount and lastPlayed
2238 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", playCount, lastPlayed.GetAsDBDateTime().c_str(), idFile);
2239 m_pDS->exec(strSQL.c_str());
2244 // and insert the new row
2245 CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
2246 sql += PrepareSQL(" where idEpisode=%i", idEpisode);
2247 m_pDS->exec(sql.c_str());
2248 CommitTransaction();
2254 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2259 int CVideoDatabase::GetSeasonId(int showID, int season)
2261 CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
2262 CStdString id = GetSingleValue("seasons", "idSeason", sql);
2265 return strtol(id.c_str(), NULL, 10);
2268 int CVideoDatabase::AddSeason(int showID, int season)
2270 int seasonId = GetSeasonId(showID, season);
2273 if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
2274 seasonId = (int)m_pDS->lastinsertid();
2279 int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
2286 idMVideo = GetMusicVideoId(strFilenameAndPath);
2289 DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
2292 // only add a new musicvideo if we don't already have a valid idMVideo
2293 // (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
2294 // be removed from the musicvideo table)
2295 idMVideo = AddMusicVideo(strFilenameAndPath);
2298 CommitTransaction();
2303 vector<int> vecDirectors;
2304 vector<int> vecGenres;
2305 vector<int> vecStudios;
2306 AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
2309 if (!details.m_artist.empty())
2311 for (unsigned int i = 0; i < details.m_artist.size(); i++)
2313 CStdString artist = details.m_artist[i];
2315 int idArtist = AddActor(artist,"");
2316 AddArtistToMusicVideo(idMVideo, idArtist);
2321 for (i = 0; i < vecGenres.size(); ++i)
2323 AddGenreToMusicVideo(idMVideo, vecGenres[i]);
2326 for (i = 0; i < vecDirectors.size(); ++i)
2328 AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
2331 for (i = 0; i < vecStudios.size(); ++i)
2333 AddStudioToMusicVideo(idMVideo, vecStudios[i]);
2337 for (unsigned int i = 0; i < details.m_tags.size(); i++)
2339 int idTag = AddTag(details.m_tags[i]);
2340 AddTagToItem(idMVideo, idTag, "musicvideo");
2343 if (details.HasStreamDetails())
2344 SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
2346 SetArtForItem(idMVideo, "musicvideo", artwork);
2348 // update our movie table (we know it was added already above)
2349 // and insert the new row
2350 CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
2351 sql += PrepareSQL(" where idMVideo=%i", idMVideo);
2352 m_pDS->exec(sql.c_str());
2353 CommitTransaction();
2359 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2364 void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
2366 // AddFile checks to make sure the file isn't already in the DB first
2367 int idFile = AddFile(strFileNameAndPath);
2370 SetStreamDetailsForFileId(details, idFile);
2373 void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
2381 m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
2383 for (int i=1; i<=details.GetVideoStreamCount(); i++)
2385 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2386 "(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration) "
2387 "VALUES (%i,%i,'%s',%f,%i,%i,%i)",
2388 idFile, (int)CStreamDetail::VIDEO,
2389 details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
2390 details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i)));
2392 for (int i=1; i<=details.GetAudioStreamCount(); i++)
2394 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2395 "(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
2396 "VALUES (%i,%i,'%s',%i,'%s')",
2397 idFile, (int)CStreamDetail::AUDIO,
2398 details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
2399 details.GetAudioLanguage(i).c_str()));
2401 for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
2403 m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
2404 "(idFile, iStreamType, strSubtitleLanguage) "
2405 "VALUES (%i,%i,'%s')",
2406 idFile, (int)CStreamDetail::SUBTITLE,
2407 details.GetSubtitleLanguage(i).c_str()));
2410 // update the runtime information, if empty
2411 if (details.GetVideoDuration())
2413 vector< pair<string, int> > tables;
2414 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
2415 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
2416 tables.push_back(make_pair("musicvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
2417 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
2419 CStdString sql = PrepareSQL("update %s set c%02d=%d where idFile=%d and c%02d=''",
2420 i->first.c_str(), i->second, details.GetVideoDuration(), idFile, i->second);
2425 CommitTransaction();
2429 RollbackTransaction();
2430 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
2434 //********************************************************************************************************************************
2435 void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
2439 if (NULL == m_pDB.get()) return ;
2440 if (NULL == m_pDS.get()) return ;
2442 if (idMovie < 0) return ;
2445 if (iType == VIDEODB_CONTENT_MOVIES)
2446 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 );
2447 if (iType == VIDEODB_CONTENT_EPISODES)
2448 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 );
2449 if (iType == VIDEODB_CONTENT_TVSHOWS)
2450 strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
2451 if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
2452 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 );
2454 m_pDS->query( strSQL.c_str() );
2457 if (iType != VIDEODB_CONTENT_TVSHOWS)
2459 CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
2460 ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
2463 filePath = m_pDS->fv("path.strPath").get_asString();
2469 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2473 //********************************************************************************************************************************
2474 void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend, long partNumber)
2478 if (URIUtils::IsStack(strFilenameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(strFilenameAndPath),false).IsDVDImage())
2480 CStackDirectory dir;
2481 CFileItemList fileList;
2482 dir.GetDirectory(strFilenameAndPath, fileList);
2485 for (int i = fileList.Size() - 1; i >= 0; i--) // put the bookmarks of the highest part first in the list
2486 GetBookMarksForFile(fileList[i]->GetPath(), bookmarks, type, true, (i+1));
2490 int idFile = GetFileId(strFilenameAndPath);
2491 if (idFile < 0) return ;
2493 bookmarks.erase(bookmarks.begin(), bookmarks.end());
2494 if (NULL == m_pDB.get()) return ;
2495 if (NULL == m_pDS.get()) return ;
2497 CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
2498 m_pDS->query( strSQL.c_str() );
2499 while (!m_pDS->eof())
2502 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2503 bookmark.partNumber = partNumber;
2504 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2505 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2506 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2507 bookmark.player = m_pDS->fv("player").get_asString();
2508 bookmark.type = type;
2509 if (type == CBookmark::EPISODE)
2511 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);
2512 m_pDS2->query(strSQL2.c_str());
2513 bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
2514 bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
2517 bookmarks.push_back(bookmark);
2520 //sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
2526 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2530 bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
2532 VECBOOKMARKS bookmarks;
2533 GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
2534 if (bookmarks.size() > 0)
2536 bookmark = bookmarks[0];
2542 void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
2544 if (!m_pDB.get() || !m_pDS.get())
2547 int fileID = GetFileId(strFilenameAndPath);
2553 CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
2554 m_pDS->exec(sql.c_str());
2558 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2562 void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
2566 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);
2567 m_pDS->query(strSQL.c_str());
2568 while (!m_pDS->eof())
2570 episodes.push_back(GetDetailsForEpisode(m_pDS));
2577 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2581 //********************************************************************************************************************************
2582 void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2586 int idFile = AddFile(strFilenameAndPath);
2589 if (NULL == m_pDB.get()) return ;
2590 if (NULL == m_pDS.get()) return ;
2594 if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
2596 strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
2598 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
2600 /* get a bookmark within the same time as previous */
2601 double mintime = bookmark.timeInSeconds - 0.5f;
2602 double maxtime = bookmark.timeInSeconds + 0.5f;
2603 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());
2606 if (type != CBookmark::EPISODE)
2609 m_pDS->query( strSQL.c_str() );
2610 if (m_pDS->num_rows() != 0)
2611 idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2614 // update or insert depending if it existed before
2615 if (idBookmark >= 0 )
2616 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);
2618 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);
2620 m_pDS->exec(strSQL.c_str());
2624 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2628 void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
2632 int idFile = GetFileId(strFilenameAndPath);
2633 if (idFile < 0) return ;
2634 if (NULL == m_pDB.get()) return ;
2635 if (NULL == m_pDS.get()) return ;
2637 /* a litle bit uggly, we clear first bookmark that is within one second of given */
2638 /* should be no problem since we never add bookmarks that are closer than that */
2639 double mintime = bookmark.timeInSeconds - 0.5f;
2640 double maxtime = bookmark.timeInSeconds + 0.5f;
2641 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);
2643 m_pDS->query( strSQL.c_str() );
2644 if (m_pDS->num_rows() != 0)
2646 int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
2647 strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
2648 m_pDS->exec(strSQL.c_str());
2649 if (type == CBookmark::EPISODE)
2651 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);
2652 m_pDS->exec(strSQL.c_str());
2660 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2664 //********************************************************************************************************************************
2665 void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
2669 int idFile = GetFileId(strFilenameAndPath);
2670 if (idFile < 0) return ;
2671 if (NULL == m_pDB.get()) return ;
2672 if (NULL == m_pDS.get()) return ;
2674 CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
2675 m_pDS->exec(strSQL.c_str());
2676 if (type == CBookmark::EPISODE)
2678 strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
2679 m_pDS->exec(strSQL.c_str());
2684 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2689 bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
2693 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);
2694 m_pDS->query( strSQL.c_str() );
2697 bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
2698 bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
2699 bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
2700 bookmark.playerState = m_pDS->fv("playerState").get_asString();
2701 bookmark.player = m_pDS->fv("player").get_asString();
2702 bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
2713 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2719 void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
2723 int idFile = GetFileId(tag.m_strFileNameAndPath);
2724 // delete the current episode for the selected episode number
2725 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);
2726 m_pDS->exec(strSQL.c_str());
2728 AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
2729 int idBookmark = (int)m_pDS->lastinsertid();
2730 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);
2731 m_pDS->exec(strSQL.c_str());
2735 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2739 void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
2743 CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2744 m_pDS->exec(strSQL.c_str());
2745 strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
2746 m_pDS->exec(strSQL.c_str());
2750 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
2754 //********************************************************************************************************************************
2755 void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
2761 GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
2763 DeleteMovie(path, bKeepId, idMovie);
2766 void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
2770 if (NULL == m_pDB.get()) return ;
2771 if (NULL == m_pDS.get()) return ;
2774 idMovie = GetMovieId(strFilenameAndPath);
2782 strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
2783 m_pDS->exec(strSQL.c_str());
2785 strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
2786 m_pDS->exec(strSQL.c_str());
2788 strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
2789 m_pDS->exec(strSQL.c_str());
2791 strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
2792 m_pDS->exec(strSQL.c_str());
2794 strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
2795 m_pDS->exec(strSQL.c_str());
2797 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2799 // keep the movie table entry, linking to tv shows, and bookmarks
2800 // so we can update the data in place
2801 // the ancilliary tables are still purged
2804 ClearBookMarksOfFile(strFilenameAndPath);
2806 strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
2807 m_pDS->exec(strSQL.c_str());
2809 strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
2810 m_pDS->exec(strSQL.c_str());
2813 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2815 AnnounceRemove("movie", idMovie);
2817 CStdString strPath, strFileName;
2818 SplitPath(strFilenameAndPath,strPath,strFileName);
2819 InvalidatePathHash(strPath);
2820 CommitTransaction();
2825 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
2829 void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
2835 GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
2837 DeleteTvShow(path, bKeepId, idTvShow);
2840 void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
2844 if (NULL == m_pDB.get()) return ;
2845 if (NULL == m_pDS.get()) return ;
2848 idTvShow = GetTvShowId(strPath);
2855 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);
2856 m_pDS2->query(strSQL.c_str());
2857 while (!m_pDS2->eof())
2859 CStdString strFilenameAndPath;
2860 CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
2861 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
2862 ConstructPath(strFilenameAndPath, strPath, strFileName);
2863 DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
2867 DeleteDetailsForTvShow(strPath, idTvShow);
2869 strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
2870 m_pDS->exec(strSQL.c_str());
2872 // keep tvshow table and movielink table so we can update data in place
2875 strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
2876 m_pDS->exec(strSQL.c_str());
2878 strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
2879 m_pDS->exec(strSQL.c_str());
2881 strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
2882 m_pDS->exec(strSQL.c_str());
2885 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2887 AnnounceRemove("tvshow", idTvShow);
2889 InvalidatePathHash(strPath);
2891 CommitTransaction();
2896 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
2900 void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
2906 GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
2908 DeleteEpisode(path, idEpisode, bKeepId);
2911 void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
2915 if (NULL == m_pDB.get()) return ;
2916 if (NULL == m_pDS.get()) return ;
2919 idEpisode = GetEpisodeId(strFilenameAndPath);
2926 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
2928 AnnounceRemove("episode", idEpisode);
2931 strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
2932 m_pDS->exec(strSQL.c_str());
2934 strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
2935 m_pDS->exec(strSQL.c_str());
2937 strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
2938 m_pDS->exec(strSQL.c_str());
2940 DeleteStreamDetails(GetFileId(strFilenameAndPath));
2942 // keep episode table entry and bookmarks so we can update the data in place
2943 // the ancilliary tables are still purged
2946 ClearBookMarksOfFile(strFilenameAndPath);
2948 strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
2949 m_pDS->exec(strSQL.c_str());
2955 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
2959 void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
2961 if (idMusicVideo < 0)
2965 GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
2967 DeleteMusicVideo(path, bKeepId, idMusicVideo);
2970 void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
2974 if (NULL == m_pDB.get()) return ;
2975 if (NULL == m_pDS.get()) return ;
2978 idMVideo = GetMusicVideoId(strFilenameAndPath);
2986 strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
2987 m_pDS->exec(strSQL.c_str());
2989 strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
2990 m_pDS->exec(strSQL.c_str());
2992 strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
2993 m_pDS->exec(strSQL.c_str());
2995 strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
2996 m_pDS->exec(strSQL.c_str());
2998 DeleteStreamDetails(GetFileId(strFilenameAndPath));
3000 // keep the music video table entry and bookmarks so we can update data in place
3001 // the ancilliary tables are still purged
3004 ClearBookMarksOfFile(strFilenameAndPath);
3006 strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
3007 m_pDS->exec(strSQL.c_str());
3010 //TODO: move this below CommitTransaction() once UPnP doesn't rely on this anymore
3012 AnnounceRemove("musicvideo", idMVideo);
3014 CStdString strPath, strFileName;
3015 SplitPath(strFilenameAndPath,strPath,strFileName);
3016 InvalidatePathHash(strPath);
3017 CommitTransaction();
3022 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3026 void CVideoDatabase::DeleteStreamDetails(int idFile)
3028 m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
3031 void CVideoDatabase::DeleteSet(int idSet)
3035 if (NULL == m_pDB.get()) return ;
3036 if (NULL == m_pDS.get()) return ;
3039 strSQL=PrepareSQL("delete from sets where idSet = %i", idSet);
3040 m_pDS->exec(strSQL.c_str());
3041 strSQL=PrepareSQL("update movie set idSet = null where idSet = %i", idSet);
3042 m_pDS->exec(strSQL.c_str());
3046 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
3050 void CVideoDatabase::ClearMovieSet(int idMovie)
3052 SetMovieSet(idMovie, -1);
3055 void CVideoDatabase::SetMovieSet(int idMovie, int idSet)
3058 ExecuteQuery(PrepareSQL("update movie set idSet = %i where idMovie = %i", idSet, idMovie));
3060 ExecuteQuery(PrepareSQL("update movie set idSet = null where idMovie = %i", idMovie));
3063 void CVideoDatabase::DeleteTag(int idTag, VIDEODB_CONTENT_TYPE mediaType)
3067 if (m_pDB.get() == NULL || m_pDS.get() == NULL)
3071 if (mediaType == VIDEODB_CONTENT_MOVIES)
3073 else if (mediaType == VIDEODB_CONTENT_TVSHOWS)
3075 else if (mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
3076 type = "musicvideo";
3081 strSQL = PrepareSQL("DELETE FROM taglinks WHERE idTag = %i AND media_type = '%s'", idTag, type.c_str());
3082 m_pDS->exec(strSQL.c_str());
3086 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idTag);
3090 void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3092 GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
3095 void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
3097 for (int i = min + 1; i < max; i++)
3099 switch (offsets[i].type)
3101 case VIDEODB_TYPE_STRING:
3102 *(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
3104 case VIDEODB_TYPE_INT:
3105 case VIDEODB_TYPE_COUNT:
3106 *(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
3108 case VIDEODB_TYPE_BOOL:
3109 *(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
3111 case VIDEODB_TYPE_FLOAT:
3112 *(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
3114 case VIDEODB_TYPE_STRINGARRAY:
3115 *(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
3117 case VIDEODB_TYPE_DATE:
3118 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
3120 case VIDEODB_TYPE_DATETIME:
3121 ((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
3127 DWORD movieTime = 0;
3130 CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
3132 CVideoInfoTag details;
3137 case VIDEODB_CONTENT_MOVIES:
3138 GetMovieInfo("", details, id);
3140 case VIDEODB_CONTENT_TVSHOWS:
3141 GetTvShowInfo("", details, id);
3143 case VIDEODB_CONTENT_EPISODES:
3144 GetEpisodeInfo("", details, id);
3146 case VIDEODB_CONTENT_MUSICVIDEOS:
3147 GetMusicVideoInfo("", details, id);
3156 bool CVideoDatabase::GetStreamDetails(CFileItem& item)
3158 // Note that this function (possibly) creates VideoInfoTags for items that don't have one yet!
3161 if (item.HasVideoInfoTag())
3162 fileId = item.GetVideoInfoTag()->m_iFileId;
3165 fileId = GetFileId(item);
3170 // Have a file id, get stream details if available (creates tag either way)
3171 item.GetVideoInfoTag()->m_iFileId = fileId;
3172 return GetStreamDetails(*item.GetVideoInfoTag());
3175 bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
3177 if (tag.m_iFileId < 0)
3180 bool retVal = false;
3182 CStreamDetails& details = tag.m_streamDetails;
3185 auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
3188 CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
3193 CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
3196 case CStreamDetail::VIDEO:
3198 CStreamDetailVideo *p = new CStreamDetailVideo();
3199 p->m_strCodec = pDS->fv(2).get_asString();
3200 p->m_fAspect = pDS->fv(3).get_asFloat();
3201 p->m_iWidth = pDS->fv(4).get_asInt();
3202 p->m_iHeight = pDS->fv(5).get_asInt();
3203 p->m_iDuration = pDS->fv(10).get_asInt();
3204 details.AddStream(p);
3208 case CStreamDetail::AUDIO:
3210 CStreamDetailAudio *p = new CStreamDetailAudio();
3211 p->m_strCodec = pDS->fv(6).get_asString();
3212 if (pDS->fv(7).get_isNull())
3213 p->m_iChannels = -1;
3215 p->m_iChannels = pDS->fv(7).get_asInt();
3216 p->m_strLanguage = pDS->fv(8).get_asString();
3217 details.AddStream(p);
3221 case CStreamDetail::SUBTITLE:
3223 CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
3224 p->m_strLanguage = pDS->fv(9).get_asString();
3225 details.AddStream(p);
3238 CLog::Log(LOGERROR, "%s(%i) failed", __FUNCTION__, tag.m_iFileId);
3240 details.DetermineBestStreams();
3242 if (details.GetVideoDuration() > 0)
3243 tag.m_duration = details.GetVideoDuration();
3248 bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag)
3250 if (tag.m_iFileId < 0)
3257 if (URIUtils::IsStack(tag.m_strFileNameAndPath) && CFileItem(CStackDirectory::GetFirstStackedFile(tag.m_strFileNameAndPath),false).IsDVDImage())
3259 CStackDirectory dir;
3260 CFileItemList fileList;
3261 dir.GetDirectory(tag.m_strFileNameAndPath, fileList);
3262 tag.m_resumePoint.Reset();
3263 for (int i = fileList.Size() - 1; i >= 0; i--)
3266 if (GetResumeBookMark(fileList[i]->GetPath(), bookmark))
3268 tag.m_resumePoint = bookmark;
3269 tag.m_resumePoint.partNumber = (i+1); /* store part number in here */
3277 CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
3278 m_pDS2->query( strSQL.c_str() );
3281 tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
3282 tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
3283 tag.m_resumePoint.partNumber = 0; // regular files or non-iso stacks don't need partNumber
3284 tag.m_resumePoint.type = CBookmark::RESUME;
3292 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
3298 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3300 return GetDetailsForMovie(pDS->get_sql_record(), getDetails);
3303 CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3305 CVideoInfoTag details;
3310 DWORD time = XbmcThreads::SystemClockMillis();
3311 int idMovie = record->at(0).get_asInt();
3313 GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
3315 details.m_iDbId = idMovie;
3316 details.m_type = "movie";
3318 details.m_iSetId = record->at(VIDEODB_DETAILS_MOVIE_SET_ID).get_asInt();
3319 details.m_strSet = record->at(VIDEODB_DETAILS_MOVIE_SET_NAME).get_asString();
3320 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3321 details.m_strPath = record->at(VIDEODB_DETAILS_MOVIE_PATH).get_asString();
3322 CStdString strFileName = record->at(VIDEODB_DETAILS_MOVIE_FILE).get_asString();
3323 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3324 details.m_playCount = record->at(VIDEODB_DETAILS_MOVIE_PLAYCOUNT).get_asInt();
3325 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_LASTPLAYED).get_asString());
3326 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MOVIE_DATEADDED).get_asString());
3327 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_RESUME_TIME).get_asInt();
3328 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MOVIE_TOTAL_TIME).get_asInt();
3329 details.m_resumePoint.type = CBookmark::RESUME;
3331 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3335 GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
3337 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3338 details.m_strPictureURL.Parse();
3341 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);
3342 m_pDS2->query(strSQL.c_str());
3343 while (!m_pDS2->eof())
3345 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3349 // create tvshowlink string
3351 GetLinksToTvShow(idMovie,links);
3352 for (unsigned int i=0;i<links.size();++i)
3354 CStdString strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
3355 VIDEODB_ID_TV_TITLE,links[i]);
3356 m_pDS2->query(strSQL.c_str());
3358 details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
3362 // get streamdetails
3363 GetStreamDetails(details);
3368 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3370 return GetDetailsForTvShow(pDS->get_sql_record(), getDetails);
3373 CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3375 CVideoInfoTag details;
3380 DWORD time = XbmcThreads::SystemClockMillis();
3381 int idTvShow = record->at(0).get_asInt();
3383 GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
3384 details.m_iDbId = idTvShow;
3385 details.m_type = "tvshow";
3386 details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
3387 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
3388 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_LASTPLAYED).get_asString());
3389 details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
3390 details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
3391 details.m_strShowPath = details.m_strPath;
3392 details.m_strShowTitle = details.m_strTitle;
3394 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3398 GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
3401 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);
3402 m_pDS2->query(strSQL.c_str());
3403 while (!m_pDS2->eof())
3405 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3409 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3410 details.m_strPictureURL.Parse();
3415 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3417 return GetDetailsForEpisode(pDS->get_sql_record(), getDetails);
3420 CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3422 CVideoInfoTag details;
3427 DWORD time = XbmcThreads::SystemClockMillis();
3428 int idEpisode = record->at(0).get_asInt();
3430 GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
3431 details.m_iDbId = idEpisode;
3432 details.m_type = "episode";
3433 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3434 details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
3435 CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
3436 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3437 details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
3438 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
3439 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
3440 details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
3441 details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
3442 details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
3443 details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
3444 details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
3445 details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
3446 details.m_iIdSeason = record->at(VIDEODB_DETAILS_EPISODE_SEASON_ID).get_asInt();
3448 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_RESUME_TIME).get_asInt();
3449 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_EPISODE_TOTAL_TIME).get_asInt();
3450 details.m_resumePoint.type = CBookmark::RESUME;
3452 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3456 GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
3457 GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
3459 castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3460 details.m_strPictureURL.Parse();
3461 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);
3462 m_pDS2->query(strSQL.c_str());
3464 details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
3467 // get streamdetails
3468 GetStreamDetails(details);
3473 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS, bool getDetails /* = false */)
3475 return GetDetailsForMusicVideo(pDS->get_sql_record(), getDetails);
3478 CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record, bool getDetails /* = false */)
3480 CVideoInfoTag details;
3482 unsigned int time = XbmcThreads::SystemClockMillis();
3483 int idMVideo = record->at(0).get_asInt();
3485 GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
3486 details.m_iDbId = idMVideo;
3487 details.m_type = "musicvideo";
3489 details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
3490 details.m_strPath = record->at(VIDEODB_DETAILS_MUSICVIDEO_PATH).get_asString();
3491 CStdString strFileName = record->at(VIDEODB_DETAILS_MUSICVIDEO_FILE).get_asString();
3492 ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
3493 details.m_playCount = record->at(VIDEODB_DETAILS_MUSICVIDEO_PLAYCOUNT).get_asInt();
3494 details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_LASTPLAYED).get_asString());
3495 details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_MUSICVIDEO_DATEADDED).get_asString());
3496 details.m_resumePoint.timeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_RESUME_TIME).get_asInt();
3497 details.m_resumePoint.totalTimeInSeconds = record->at(VIDEODB_DETAILS_MUSICVIDEO_TOTAL_TIME).get_asInt();
3498 details.m_resumePoint.type = CBookmark::RESUME;
3500 movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
3505 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);
3506 m_pDS2->query(strSQL.c_str());
3507 while (!m_pDS2->eof())
3509 details.m_tags.push_back(m_pDS2->fv("tag.strTag").get_asString());
3514 details.m_strPictureURL.Parse();
3516 // get streamdetails
3517 GetStreamDetails(details);
3522 void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
3526 if (!m_pDB.get()) return;
3527 if (!m_pDS2.get()) return;
3529 CStdString sql = PrepareSQL("SELECT actors.strActor,"
3530 " actorlink%s.strRole,"
3535 " actorlink%s.idActor=actors.idActor"
3537 " art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
3538 "WHERE actorlink%s.%s=%i "
3539 "ORDER BY actorlink%s.iOrder",table.c_str(), table.c_str(), table.c_str(), table.c_str(), table_id.c_str(), type_id, table.c_str());
3540 m_pDS2->query(sql.c_str());
3541 while (!m_pDS2->eof())
3544 info.strName = m_pDS2->fv(0).get_asString();
3546 for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
3548 if (i->strName == info.strName)
3556 info.strRole = m_pDS2->fv(1).get_asString();
3557 info.thumbUrl.ParseString(m_pDS2->fv(2).get_asString());
3558 info.thumb = m_pDS2->fv(3).get_asString();
3559 cast.push_back(info);
3567 CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
3571 /// \brief GetVideoSettings() obtains any saved video settings for the current file.
3572 /// \retval Returns true if the settings exist, false otherwise.
3573 bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
3577 // obtain the FileID (if it exists)
3578 #ifdef NEW_VIDEODB_METHODS
3579 if (NULL == m_pDB.get()) return false;
3580 if (NULL == m_pDS.get()) return false;
3581 CStdString strPath, strFileName;
3582 URIUtils::Split(strFilenameAndPath, strPath, strFileName);
3583 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());
3585 int idFile = GetFileId(strFilenameAndPath);
3586 if (idFile < 0) return false;
3587 if (NULL == m_pDB.get()) return false;
3588 if (NULL == m_pDS.get()) return false;
3589 // ok, now obtain the settings for this file
3590 CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
3592 m_pDS->query( strSQL.c_str() );
3593 if (m_pDS->num_rows() > 0)
3594 { // get the video settings info
3595 settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
3596 settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
3597 settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
3598 settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
3599 settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
3600 settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
3601 settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
3602 settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
3603 settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
3604 settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
3605 settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
3606 settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
3607 settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
3608 settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
3609 settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
3610 settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
3611 settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
3612 settings.m_Crop = m_pDS->fv("Crop").get_asBool();
3613 settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
3614 settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
3615 settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
3616 settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
3617 settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
3618 settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
3619 settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
3620 settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
3621 settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
3622 settings.m_StereoMode = m_pDS->fv("StereoMode").get_asInt();
3623 settings.m_StereoInvert = m_pDS->fv("StereoInvert").get_asBool();
3624 settings.m_SubtitleCached = false;
3632 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3637 /// \brief Sets the settings for a particular video file
3638 void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
3642 if (NULL == m_pDB.get()) return ;
3643 if (NULL == m_pDS.get()) return ;
3644 int idFile = AddFile(strFilenameAndPath);
3648 strSQL.Format("select * from settings where idFile=%i", idFile);
3649 m_pDS->query( strSQL.c_str() );
3650 if (m_pDS->num_rows() > 0)
3654 strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
3655 "AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
3656 "VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
3657 "DeinterlaceMode=%i,",
3658 setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3659 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
3660 setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
3661 setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
3662 setting.m_DeinterlaceMode);
3664 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);
3666 m_pDS->exec(strSQL.c_str());
3672 strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
3673 "AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
3674 "Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
3675 "ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
3676 "Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode,StereoMode,StereoInvert) "
3678 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)",
3679 idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
3680 setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
3681 setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
3682 setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
3683 setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
3684 setting.m_DeinterlaceMode, setting.m_StereoMode, setting.m_StereoInvert);
3685 m_pDS->exec(strSQL.c_str());
3690 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
3694 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
3696 for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
3697 SetArtForItem(mediaId, mediaType, i->first, i->second);
3700 void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
3704 if (NULL == m_pDB.get()) return;
3705 if (NULL == m_pDS.get()) return;
3707 // don't set <foo>.<bar> art types - these are derivative types from parent items
3708 if (artType.find('.') != string::npos)
3711 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());
3712 m_pDS->query(sql.c_str());
3715 int artId = m_pDS->fv(0).get_asInt();
3717 sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
3718 m_pDS->exec(sql.c_str());
3723 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());
3724 m_pDS->exec(sql.c_str());
3729 CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
3733 bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
3737 if (NULL == m_pDB.get()) return false;
3738 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3740 CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
3741 m_pDS2->query(sql.c_str());
3742 while (!m_pDS2->eof())
3744 art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
3748 return !art.empty();
3752 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
3757 string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
3759 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());
3760 return GetSingleValue(query, m_pDS2);
3763 bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, map<string, string> > &seasonArt)
3767 if (NULL == m_pDB.get()) return false;
3768 if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
3770 // get all seasons for this show
3771 CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
3772 m_pDS2->query(sql.c_str());
3774 vector< pair<int, int> > seasons;
3775 while (!m_pDS2->eof())
3777 seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
3782 for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
3784 map<string, string> art;
3785 GetArtForItem(i->first, "season", art);
3786 seasonArt.insert(make_pair(i->second,art));
3792 CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
3797 bool CVideoDatabase::GetArtTypes(const std::string &mediaType, std::vector<std::string> &artTypes)
3801 if (NULL == m_pDB.get()) return false;
3802 if (NULL == m_pDS.get()) return false;
3804 CStdString sql = PrepareSQL("SELECT DISTINCT type FROM art WHERE media_type='%s'", mediaType.c_str());
3805 int numRows = RunQuery(sql);
3807 return numRows == 0;
3809 while (!m_pDS->eof())
3811 artTypes.push_back(m_pDS->fv(0).get_asString());
3819 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, mediaType.c_str());
3824 /// \brief GetStackTimes() obtains any saved video times for the stacked file
3825 /// \retval Returns true if the stack times exist, false otherwise.
3826 bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> ×)
3830 // obtain the FileID (if it exists)
3831 int idFile = GetFileId(filePath);
3832 if (idFile < 0) return false;
3833 if (NULL == m_pDB.get()) return false;
3834 if (NULL == m_pDS.get()) return false;
3835 // ok, now obtain the settings for this file
3836 CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
3837 m_pDS->query( strSQL.c_str() );
3838 if (m_pDS->num_rows() > 0)
3839 { // get the video settings info
3840 CStdStringArray timeString;
3842 StringUtils::SplitString(m_pDS->fv("times").get_asString(), ",", timeString);
3844 for (unsigned int i = 0; i < timeString.size(); i++)
3846 times.push_back(atoi(timeString[i].c_str()));
3847 timeTotal += atoi(timeString[i].c_str());
3850 return (timeTotal > 0);
3856 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
3861 /// \brief Sets the stack times for a particular video file
3862 void CVideoDatabase::SetStackTimes(const CStdString& filePath, vector<int> ×)
3866 if (NULL == m_pDB.get()) return ;
3867 if (NULL == m_pDS.get()) return ;
3868 int idFile = AddFile(filePath);
3872 // delete any existing items
3873 m_pDS->exec( PrepareSQL("delete from stacktimes where idFile=%i", idFile) );
3876 CStdString timeString;
3877 timeString.Format("%i", times[0]);
3878 for (unsigned int i = 1; i < times.size(); i++)
3881 time.Format(",%i", times[i]);
3884 m_pDS->exec( PrepareSQL("insert into stacktimes (idFile,times) values (%i,'%s')\n", idFile, timeString.c_str()) );
3888 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
3892 void CVideoDatabase::RemoveContentForPath(const CStdString& strPath, CGUIDialogProgress *progress /* = NULL */)
3894 if(URIUtils::IsMultiPath(strPath))
3896 vector<CStdString> paths;
3897 CMultiPathDirectory::GetPaths(strPath, paths);
3899 for(unsigned i=0;i<paths.size();i++)
3900 RemoveContentForPath(paths[i], progress);
3905 if (NULL == m_pDB.get()) return ;
3906 if (NULL == m_pDS.get()) return ;
3910 progress->SetHeading(700);
3911 progress->SetLine(0, "");
3912 progress->SetLine(1, 313);
3913 progress->SetLine(2, 330);
3914 progress->SetPercentage(0);
3915 progress->StartModal();
3916 progress->ShowProgressBar(true);
3918 vector< pair<int,string> > paths;
3919 GetSubPaths(strPath, paths);
3921 for (vector< pair<int, string> >::const_iterator i = paths.begin(); i != paths.end(); ++i)
3923 bool bMvidsChecked=false;
3926 progress->SetPercentage((int)((float)(iCurr++)/paths.size()*100.f));
3927 progress->Progress();
3930 if (HasTvShowInfo(i->second))
3931 DeleteTvShow(i->second);
3934 CStdString strSQL = PrepareSQL("select files.strFilename from files join movie on movie.idFile=files.idFile where files.idPath=%i", i->first);
3935 m_pDS2->query(strSQL.c_str());
3938 strSQL = PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
3939 m_pDS2->query(strSQL.c_str());
3940 bMvidsChecked = true;
3942 while (!m_pDS2->eof())
3944 CStdString strMoviePath;
3945 CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
3946 ConstructPath(strMoviePath, i->second, strFileName);
3947 if (HasMovieInfo(strMoviePath))
3948 DeleteMovie(strMoviePath);
3949 if (HasMusicVideoInfo(strMoviePath))
3950 DeleteMusicVideo(strMoviePath);
3952 if (m_pDS2->eof() && !bMvidsChecked)
3954 strSQL =PrepareSQL("select files.strFilename from files join musicvideo on musicvideo.idFile=files.idFile where files.idPath=%i", i->first);
3955 m_pDS2->query(strSQL.c_str());
3956 bMvidsChecked = true;
3960 m_pDS2->exec(PrepareSQL("update path set strContent='', strScraper='', strHash='',strSettings='',useFolderNames=0,scanRecursive=0 where idPath=%i", i->first));
3966 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
3972 void CVideoDatabase::SetScraperForPath(const CStdString& filePath, const ScraperPtr& scraper, const VIDEO::SScanSettings& settings)
3974 // if we have a multipath, set scraper for all contained paths too
3975 if(URIUtils::IsMultiPath(filePath))
3977 vector<CStdString> paths;
3978 CMultiPathDirectory::GetPaths(filePath, paths);
3980 for(unsigned i=0;i<paths.size();i++)
3981 SetScraperForPath(paths[i],scraper,settings);
3986 if (NULL == m_pDB.get()) return ;
3987 if (NULL == m_pDS.get()) return ;
3989 int idPath = AddPath(filePath);
3995 if (settings.exclude)
3996 { //NB See note in ::GetScraperForPath about strContent=='none'
3997 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0 , exclude=1 where idPath=%i", idPath);
4000 { // catch clearing content, but not excluding
4001 strSQL=PrepareSQL("update path set strContent='', strScraper='', scanRecursive=0, useFolderNames=0, strSettings='', noUpdate=0, exclude=0 where idPath=%i", idPath);
4005 CStdString content = TranslateContent(scraper->Content());
4006 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);
4008 m_pDS->exec(strSQL.c_str());
4012 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, filePath.c_str());
4016 bool CVideoDatabase::ScraperInUse(const CStdString &scraperID) const
4020 if (NULL == m_pDB.get()) return false;
4021 if (NULL == m_pDS.get()) return false;
4023 CStdString sql = PrepareSQL("select count(1) from path where strScraper='%s'", scraperID.c_str());
4024 if (!m_pDS->query(sql.c_str()) || m_pDS->num_rows() == 0)
4026 bool found = m_pDS->fv(0).get_asInt() > 0;
4032 CLog::Log(LOGERROR, "%s(%s) failed", __FUNCTION__, scraperID.c_str());
4040 CArtItem() { art_id = 0; media_id = 0; };
4048 bool CVideoDatabase::UpdateOldVersion(int iVersion)
4052 m_pDS->exec("ALTER TABLE settings ADD VerticalShift float");
4056 // only if MySQL is used and default character set is not utf8
4057 // string data needs to be converted to proper utf8
4058 CStdString charset = m_pDS->getDatabase()->getDefaultCharset();
4059 if (!m_sqlite && !charset.empty() && charset != "utf8")
4061 map<CStdString, CStdStringArray> tables;
4062 map<CStdString, CStdStringArray>::iterator itt;
4063 CStdStringArray::iterator itc;
4065 // columns that need to be converted
4067 CStdStringArray c_columns;
4068 for (int i = 0; i < 22; i++)
4071 c.Format("c%02d", i);
4072 c_columns.push_back(c);
4075 tables.insert(pair<CStdString, CStdStringArray> ("episode", c_columns));
4076 tables.insert(pair<CStdString, CStdStringArray> ("movie", c_columns));
4077 tables.insert(pair<CStdString, CStdStringArray> ("musicvideo", c_columns));
4078 tables.insert(pair<CStdString, CStdStringArray> ("tvshow", c_columns));
4082 c1.push_back("strRole");
4083 tables.insert(pair<CStdString, CStdStringArray> ("actorlinkepisode", c1));
4084 tables.insert(pair<CStdString, CStdStringArray> ("actorlinkmovie", c1));
4085 tables.insert(pair<CStdString, CStdStringArray> ("actorlinktvshow", c1));
4089 c2.push_back("strActor");
4090 tables.insert(pair<CStdString, CStdStringArray> ("actors", c2));
4093 c3.push_back("strCountry");
4094 tables.insert(pair<CStdString, CStdStringArray> ("country", c3));
4097 c4.push_back("strFilename");
4098 tables.insert(pair<CStdString, CStdStringArray> ("files", c4));
4101 c5.push_back("strGenre");
4102 tables.insert(pair<CStdString, CStdStringArray> ("genre", c5));
4105 c6.push_back("strSet");
4106 tables.insert(pair<CStdString, CStdStringArray> ("sets", c6));
4109 c7.push_back("strStudio");
4110 tables.insert(pair<CStdString, CStdStringArray> ("studio", c7));
4113 c8.push_back("strPath");
4114 tables.insert(pair<CStdString, CStdStringArray> ("path", c8));
4116 for (itt = tables.begin(); itt != tables.end(); ++itt)
4119 q = PrepareSQL("UPDATE `%s` SET", itt->first.c_str());
4120 for (itc = itt->second.begin(); itc != itt->second.end(); ++itc)
4122 q += PrepareSQL(" `%s` = CONVERT(CAST(CONVERT(`%s` USING %s) AS BINARY) USING utf8)",
4123 itc->c_str(), itc->c_str(), charset.c_str());
4124 if (*itc != itt->second.back())
4135 m_pDS->exec("ALTER TABLE movie ADD c22 text");
4136 m_pDS->exec("ALTER TABLE episode ADD c22 text");
4137 m_pDS->exec("ALTER TABLE musicvideo ADD c22 text");
4138 m_pDS->exec("ALTER TABLE tvshow ADD c22 text");
4139 // Now update our tables
4140 UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH);
4141 UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH);
4142 UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH);
4143 UpdateBasePath("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, true);
4146 { // add indices for dir entry lookups
4147 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c22(255) )");
4148 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c13(255) )");
4149 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c18(255) )");
4150 m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c16(255) )");
4154 m_pDS->exec("ALTER TABLE settings ADD ScalingMethod integer");
4155 m_pDS->exec(PrepareSQL("UPDATE settings set ScalingMethod=%i", CMediaSettings::Get().GetDefaultVideoSettings().m_ScalingMethod));
4159 // Add iOrder fields to actorlink* tables to be able to list
4160 // actors by importance
4161 m_pDS->exec("ALTER TABLE actorlinkmovie ADD iOrder integer");
4162 m_pDS->exec("ALTER TABLE actorlinktvshow ADD iOrder integer");
4163 m_pDS->exec("ALTER TABLE actorlinkepisode ADD iOrder integer");
4166 { // Add basepath link to path table for faster content retrieval, and indicies
4167 m_pDS->exec("ALTER TABLE movie ADD c23 text");
4168 m_pDS->exec("ALTER TABLE episode ADD c23 text");
4169 m_pDS->exec("ALTER TABLE musicvideo ADD c23 text");
4170 m_pDS->exec("ALTER TABLE tvshow ADD c23 text");
4171 m_pDS->dropIndex("movie", "ixMovieBasePath");
4172 m_pDS->dropIndex("musicvideo", "ixMusicVideoBasePath");
4173 m_pDS->dropIndex("episode", "ixEpisodeBasePath");
4174 m_pDS->dropIndex("tvshow", "ixTVShowBasePath");
4175 m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
4176 m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
4177 m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
4178 m_pDS->exec("CREATE INDEX ixTVShowBasePath ON tvshow ( c17(12) )");
4179 // now update the base path links
4180 UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4181 UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4182 UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4183 UpdateBasePathID("tvshow", "idShow", VIDEODB_ID_TV_BASEPATH, VIDEODB_ID_TV_PARENTPATHID);
4186 { // Change INDEX for bookmark table
4187 m_pDS->dropIndex("bookmark", "ix_bookmark");
4188 m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
4192 m_pDS->exec("ALTER TABLE settings ADD DeinterlaceMode integer");
4193 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 2 WHERE Deinterlace NOT IN (0,1)"); // anything other than none: method auto => mode force
4194 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 1 WHERE Deinterlace = 1"); // method auto => mode auto
4195 m_pDS->exec("UPDATE settings SET DeinterlaceMode = 0, Deinterlace = 1 WHERE Deinterlace = 0"); // method none => mode off, method auto
4199 { // base paths for video_ts and bdmv files was wrong (and inconsistent depending on where and when they were scanned)
4200 CStdString where = PrepareSQL(" WHERE files.strFileName LIKE 'VIDEO_TS.IFO' or files.strFileName LIKE 'index.BDMV'");
4201 UpdateBasePath("movie", "idMovie", VIDEODB_ID_BASEPATH, false, where);
4202 UpdateBasePath("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, false, where);
4203 UpdateBasePath("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, false, where);
4204 UpdateBasePathID("movie", "idMovie", VIDEODB_ID_BASEPATH, VIDEODB_ID_PARENTPATHID);
4205 UpdateBasePathID("musicvideo", "idMVideo", VIDEODB_ID_MUSICVIDEO_BASEPATH, VIDEODB_ID_MUSICVIDEO_PARENTPATHID);
4206 UpdateBasePathID("episode", "idEpisode", VIDEODB_ID_EPISODE_BASEPATH, VIDEODB_ID_EPISODE_PARENTPATHID);
4210 m_pDS->exec("ALTER TABLE path ADD dateAdded text");
4211 m_pDS->exec("ALTER TABLE files ADD dateAdded text");
4214 { // add seasons table
4215 m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
4216 m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
4217 // insert all seasons for each show
4218 m_pDS->query("SELECT idShow FROM tvshow");
4219 while (!m_pDS->eof())
4221 CStdString sql = PrepareSQL("INSERT INTO seasons (idShow,season)"
4226 " WHERE idShow=%i", VIDEODB_ID_EPISODE_SEASON, m_pDS->fv(0).get_asInt());
4227 m_pDS2->exec(sql.c_str());
4228 // and the "all seasons node"
4229 sql = PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,-1)", m_pDS->fv(0).get_asInt());
4230 m_pDS2->exec(sql.c_str());
4236 m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
4237 m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
4238 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");
4239 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");
4240 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");
4241 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");
4242 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");
4243 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");
4244 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");
4246 CMediaSettings::Get().SetVideoNeedsUpdate(63);
4247 CSettings::Get().Save();
4250 { // add idShow to episode table
4251 m_pDS->exec("ALTER TABLE episode ADD idShow integer");
4252 m_pDS->query("SELECT idEpisode FROM episode");
4253 while (!m_pDS->eof())
4255 int idEpisode = m_pDS->fv(0).get_asInt();
4256 CStdString update = PrepareSQL("UPDATE episode SET idShow=(SELECT idShow FROM tvshowlinkepisode WHERE idEpisode=%d) WHERE idEpisode=%d", idEpisode, idEpisode);
4257 m_pDS2->exec(update.c_str());
4260 m_pDS->exec("DROP TABLE tvshowlinkepisode");
4261 m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
4262 m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
4266 m_pDS->exec("CREATE TABLE tag (idTag integer primary key, strTag text)");
4267 m_pDS->exec("CREATE UNIQUE INDEX ix_tag_1 ON tag (strTag(255))");
4269 m_pDS->exec("CREATE TABLE taglinks (idTag integer, idMedia integer, media_type TEXT)");
4270 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_1 ON taglinks (idTag, media_type(20), idMedia)");
4271 m_pDS->exec("CREATE UNIQUE INDEX ix_taglinks_2 ON taglinks (idMedia, media_type(20), idTag)");
4272 m_pDS->exec("CREATE INDEX ix_taglinks_3 ON taglinks (media_type(20))");
4275 { // add idSet to movie table
4276 m_pDS->exec("ALTER TABLE movie ADD idSet integer");
4277 m_pDS->query("SELECT idMovie FROM movie");
4278 while (!m_pDS->eof())
4280 int idMovie = m_pDS->fv(0).get_asInt();
4281 CStdString sql = PrepareSQL("UPDATE movie SET idSet=(SELECT idSet FROM setlinkmovie WHERE idMovie = %d LIMIT 1) WHERE idMovie = %d", idMovie, idMovie);
4282 m_pDS2->exec(sql.c_str());
4285 m_pDS->exec("DROP TABLE IF EXISTS setlinkmovie");
4288 { // update old art URLs
4289 m_pDS->query("select art_id,url from art where url like 'image://%%'");
4290 vector< pair<int, string> > art;
4291 while (!m_pDS->eof())
4293 art.push_back(make_pair(m_pDS->fv(0).get_asInt(), CURL(m_pDS->fv(1).get_asString()).Get()));
4297 for (vector< pair<int, string> >::iterator i = art.begin(); i != art.end(); ++i)
4298 m_pDS->exec(PrepareSQL("update art set url='%s' where art_id=%d", i->second.c_str(), i->first));
4301 { // update URL encoded paths
4302 m_pDS->query("select idFile, strFilename from files");
4303 vector< pair<int, string> > files;
4304 while (!m_pDS->eof())
4306 files.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
4311 for (vector< pair<int, string> >::iterator i = files.begin(); i != files.end(); ++i)
4313 std::string filename = i->second;
4314 bool update = URIUtils::UpdateUrlEncoding(filename) &&
4315 (!m_pDS->query(PrepareSQL("SELECT idFile FROM files WHERE strFilename = '%s'", filename.c_str())) || m_pDS->num_rows() <= 0);
4319 m_pDS->exec(PrepareSQL("UPDATE files SET strFilename='%s' WHERE idFile=%d", filename.c_str(), i->first));
4323 { // Update thumb to poster or banner as applicable
4324 CTextureDatabase db;
4327 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')");
4328 vector<CArtItem> art;
4329 while (!m_pDS->eof())
4331 CTextureDetails details;
4332 if (db.GetCachedTexture(m_pDS->fv(1).get_asString(), details))
4335 item.art_id = m_pDS->fv(0).get_asInt();
4336 item.art_url = m_pDS->fv(1).get_asString();
4337 item.art_type = CVideoInfoScanner::GetArtTypeFromSize(details.width, details.height);
4338 item.media_id = m_pDS->fv(2).get_asInt();
4339 item.media_type = m_pDS->fv(3).get_asString();
4340 if (item.art_type != "thumb")
4341 art.push_back(item);
4346 for (vector<CArtItem>::iterator i = art.begin(); i != art.end(); ++i)
4348 if (GetArtForItem(i->media_id, i->media_type, i->art_type).empty())
4349 m_pDS->exec(PrepareSQL("update art set type='%s' where art_id=%d", i->art_type.c_str(), i->art_id));
4351 m_pDS->exec(PrepareSQL("delete from art where art_id=%d", i->art_id));
4357 m_pDS->exec("DROP TRIGGER IF EXISTS delete_movie");
4358 m_pDS->exec("DROP TRIGGER IF EXISTS delete_tvshow");
4359 m_pDS->exec("DROP TRIGGER IF EXISTS delete_musicvideo");
4360 m_pDS->exec("DROP TRIGGER IF EXISTS delete_episode");
4361 m_pDS->exec("DROP TRIGGER IF EXISTS delete_season");
4362 m_pDS->exec("DROP TRIGGER IF EXISTS delete_set");
4363 m_pDS->exec("DROP TRIGGER IF EXISTS delete_person");
4365 m_pDS->exec("CREATE TRIGGER delete_movie AFTER DELETE ON movie FOR EACH ROW BEGIN "
4366 "DELETE FROM art WHERE media_id=old.idMovie AND media_type='movie'; "
4367 "DELETE FROM taglinks WHERE idMedia=old.idMovie AND media_type='movie'; "
4369 m_pDS->exec("CREATE TRIGGER delete_tvshow AFTER DELETE ON tvshow FOR EACH ROW BEGIN "
4370 "DELETE FROM art WHERE media_id=old.idShow AND media_type='tvshow'; "
4371 "DELETE FROM taglinks WHERE idMedia=old.idShow AND media_type='tvshow'; "
4373 m_pDS->exec("CREATE TRIGGER delete_musicvideo AFTER DELETE ON musicvideo FOR EACH ROW BEGIN "
4374 "DELETE FROM art WHERE media_id=old.idMVideo AND media_type='musicvideo'; "
4375 "DELETE FROM taglinks WHERE idMedia=old.idMVideo AND media_type='musicvideo'; "
4377 m_pDS->exec("CREATE TRIGGER delete_episode AFTER DELETE ON episode FOR EACH ROW BEGIN "
4378 "DELETE FROM art WHERE media_id=old.idEpisode AND media_type='episode'; "
4380 m_pDS->exec("CREATE TRIGGER delete_season AFTER DELETE ON seasons FOR EACH ROW BEGIN "
4381 "DELETE FROM art WHERE media_id=old.idSeason AND media_type='season'; "
4383 m_pDS->exec("CREATE TRIGGER delete_set AFTER DELETE ON sets FOR EACH ROW BEGIN "
4384 "DELETE FROM art WHERE media_id=old.idSet AND media_type='set'; "
4386 m_pDS->exec("CREATE TRIGGER delete_person AFTER DELETE ON actors FOR EACH ROW BEGIN "
4387 "DELETE FROM art WHERE media_id=old.idActor AND media_type IN ('actor','artist','writer','director'); "
4389 m_pDS->exec("CREATE TRIGGER delete_tag AFTER DELETE ON taglinks FOR EACH ROW BEGIN "
4390 "DELETE FROM tag WHERE idTag=old.idTag AND idTag NOT IN (SELECT DISTINCT idTag FROM taglinks); "
4394 { // update the runtime columns
4395 vector< pair<string, int> > tables;
4396 tables.push_back(make_pair("movie", VIDEODB_ID_RUNTIME));
4397 tables.push_back(make_pair("episode", VIDEODB_ID_EPISODE_RUNTIME));
4398 tables.push_back(make_pair("mvideo", VIDEODB_ID_MUSICVIDEO_RUNTIME));
4399 for (vector< pair<string, int> >::iterator i = tables.begin(); i != tables.end(); ++i)
4401 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);
4402 m_pDS->query(sql.c_str());
4403 vector< pair<int, int> > videos;
4404 while (!m_pDS->eof())
4406 int duration = CVideoInfoTag::GetDurationFromMinuteString(m_pDS->fv(1).get_asString());
4408 videos.push_back(make_pair(m_pDS->fv(0).get_asInt(), duration));
4412 for (vector< pair<int, int> >::iterator j = videos.begin(); j != videos.end(); ++j)
4413 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));
4417 { // make indices on path, file non-unique (mysql has a prefix index, and prefixes aren't necessarily unique)
4418 m_pDS->dropIndex("path", "ix_path");
4419 m_pDS->dropIndex("files", "ix_files");
4420 m_pDS->exec("CREATE INDEX ix_path ON path ( strPath(255) )");
4421 m_pDS->exec("CREATE INDEX ix_files ON files ( idPath, strFilename(255) )");
4425 m_pDS->exec("ALTER TABLE settings ADD StereoMode integer");
4426 m_pDS->exec("ALTER TABLE settings ADD StereoInvert bool");
4428 // always recreate the view after any table change
4433 int CVideoDatabase::GetMinVersion() const
4438 bool CVideoDatabase::LookupByFolders(const CStdString &path, bool shows)
4440 SScanSettings settings;
4441 bool foundDirectly = false;
4442 ScraperPtr scraper = GetScraperForPath(path, settings, foundDirectly);
4443 if (scraper && scraper->Content() == CONTENT_TVSHOWS && !shows)
4444 return false; // episodes
4445 return settings.parent_name_root; // shows, movies, musicvids
4448 void CVideoDatabase::UpdateBasePath(const char *table, const char *id, int column, bool shows, const CStdString &where)
4452 query = PrepareSQL("SELECT idShow,path.strPath from tvshowlinkpath join path on tvshowlinkpath.idPath=path.idPath");
4454 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);
4457 map<CStdString, bool> paths;
4458 m_pDS2->query(query.c_str());
4459 while (!m_pDS2->eof())
4461 CStdString path(m_pDS2->fv(1).get_asString());
4462 map<CStdString, bool>::iterator i = paths.find(path);
4463 if (i == paths.end())
4465 paths.insert(make_pair(path, LookupByFolders(path, shows)));
4466 i = paths.find(path);
4468 CStdString filename;
4470 ConstructPath(filename, path, m_pDS2->fv(2).get_asString());
4473 CFileItem item(filename, shows);
4474 path = item.GetBaseMoviePath(i->second);
4475 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());
4476 m_pDS->exec(sql.c_str());
4482 void CVideoDatabase::UpdateBasePathID(const char *table, const char *id, int column, int idColumn)
4484 CStdString query = PrepareSQL("SELECT %s,c%02d from %s", id, column, table);
4485 m_pDS2->query(query.c_str());
4486 while (!m_pDS2->eof())
4488 int rowID = m_pDS2->fv(0).get_asInt();
4489 CStdString path(m_pDS2->fv(1).get_asString());
4490 // find the parent path of this item
4491 int pathID = AddPath(URIUtils::GetParentPath(path));
4494 CStdString sql = PrepareSQL("UPDATE %s SET c%02d=%d WHERE %s=%d", table, idColumn, pathID, id, rowID);
4495 m_pDS->exec(sql.c_str());
4502 bool CVideoDatabase::GetPlayCounts(const CStdString &strPath, CFileItemList &items)
4504 if(URIUtils::IsMultiPath(strPath))
4506 vector<CStdString> paths;
4507 CMultiPathDirectory::GetPaths(strPath, paths);
4510 for(unsigned i=0;i<paths.size();i++)
4511 ret |= GetPlayCounts(paths[i], items);
4516 if (URIUtils::IsPlugin(strPath))
4519 pathID = GetPathId(url.GetWithoutFilename());
4522 pathID = GetPathId(strPath);
4524 return false; // path (and thus files) aren't in the database
4529 if (NULL == m_pDB.get()) return false;
4530 if (NULL == m_pDS.get()) return false;
4532 // TODO: also test a single query for the above and below
4533 CStdString sql = PrepareSQL(
4535 " files.strFilename, files.playCount,"
4536 " bookmark.timeInSeconds, bookmark.totalTimeInSeconds "
4538 " LEFT JOIN bookmark ON"
4539 " files.idFile = bookmark.idFile AND bookmark.type = %i"
4540 " WHERE files.idPath=%i", (int)CBookmark::RESUME, pathID);
4542 if (RunQuery(sql) <= 0)
4545 items.SetFastLookup(true); // note: it's possibly quicker the other way around (map on db returned items)?
4546 while (!m_pDS->eof())
4549 ConstructPath(path, strPath, m_pDS->fv(0).get_asString());
4550 CFileItemPtr item = items.Get(path);
4553 item->GetVideoInfoTag()->m_playCount = m_pDS->fv(1).get_asInt();
4554 if (!item->GetVideoInfoTag()->m_resumePoint.IsSet())
4556 item->GetVideoInfoTag()->m_resumePoint.timeInSeconds = m_pDS->fv(2).get_asInt();
4557 item->GetVideoInfoTag()->m_resumePoint.totalTimeInSeconds = m_pDS->fv(3).get_asInt();
4558 item->GetVideoInfoTag()->m_resumePoint.type = CBookmark::RESUME;
4567 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4572 int CVideoDatabase::GetPlayCount(const CFileItem &item)
4574 int id = GetFileId(item);
4576 return 0; // not in db, so not watched
4581 if (NULL == m_pDB.get()) return -1;
4582 if (NULL == m_pDS.get()) return -1;
4584 CStdString strSQL = PrepareSQL("select playCount from files WHERE idFile=%i", id);
4586 if (m_pDS->query(strSQL.c_str()))
4588 // there should only ever be one row returned
4589 if (m_pDS->num_rows() == 1)
4590 count = m_pDS->fv(0).get_asInt();
4597 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4602 void CVideoDatabase::UpdateFanart(const CFileItem &item, VIDEODB_CONTENT_TYPE type)
4604 if (NULL == m_pDB.get()) return;
4605 if (NULL == m_pDS.get()) return;
4606 if (!item.HasVideoInfoTag() || item.GetVideoInfoTag()->m_iDbId < 0) return;
4609 if (type == VIDEODB_CONTENT_TVSHOWS)
4610 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);
4611 else if (type == VIDEODB_CONTENT_MOVIES)
4612 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);
4616 m_pDS->exec(exec.c_str());
4618 if (type == VIDEODB_CONTENT_TVSHOWS)
4619 AnnounceUpdate("tvshow", item.GetVideoInfoTag()->m_iDbId);
4620 else if (type == VIDEODB_CONTENT_MOVIES)
4621 AnnounceUpdate("movie", item.GetVideoInfoTag()->m_iDbId);
4625 CLog::Log(LOGERROR, "%s - error updating fanart for %s", __FUNCTION__, item.GetPath().c_str());
4629 void CVideoDatabase::SetPlayCount(const CFileItem &item, int count, const CDateTime &date)
4632 if (item.HasProperty("original_listitem_url") &&
4633 URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString()))
4635 CFileItem item2(item);
4636 item2.SetPath(item.GetProperty("original_listitem_url").asString());
4637 id = AddFile(item2);
4644 // and mark as watched
4647 if (NULL == m_pDB.get()) return ;
4648 if (NULL == m_pDS.get()) return ;
4653 if (!date.IsValid())
4654 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, CDateTime::GetCurrentDateTime().GetAsDBDateTime().c_str(), id);
4656 strSQL = PrepareSQL("update files set playCount=%i,lastPlayed='%s' where idFile=%i", count, date.GetAsDBDateTime().c_str(), id);
4660 if (!date.IsValid())
4661 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed=NULL where idFile=%i", id);
4663 strSQL = PrepareSQL("update files set playCount=NULL,lastPlayed='%s' where idFile=%i", date.GetAsDBDateTime().c_str(), id);
4666 m_pDS->exec(strSQL.c_str());
4668 // We only need to announce changes to video items in the library
4669 if (item.HasVideoInfoTag() && item.GetVideoInfoTag()->m_iDbId > 0)
4671 // Only provide the "playcount" value if it has actually changed
4672 if (item.GetVideoInfoTag()->m_playCount != count)
4675 data["playcount"] = count;
4676 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)), data);
4679 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", CFileItemPtr(new CFileItem(item)));
4684 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4688 void CVideoDatabase::IncrementPlayCount(const CFileItem &item)
4690 SetPlayCount(item, GetPlayCount(item) + 1);
4693 void CVideoDatabase::UpdateLastPlayed(const CFileItem &item)
4695 SetPlayCount(item, GetPlayCount(item), CDateTime::GetCurrentDateTime());
4698 void CVideoDatabase::UpdateMovieTitle(int idMovie, const CStdString& strNewMovieTitle, VIDEODB_CONTENT_TYPE iType)
4702 if (NULL == m_pDB.get()) return ;
4703 if (NULL == m_pDS.get()) return ;
4705 if (iType == VIDEODB_CONTENT_MOVIES)
4707 CLog::Log(LOGINFO, "Changing Movie:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4710 else if (iType == VIDEODB_CONTENT_EPISODES)
4712 CLog::Log(LOGINFO, "Changing Episode:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4713 content = "episode";
4715 else if (iType == VIDEODB_CONTENT_TVSHOWS)
4717 CLog::Log(LOGINFO, "Changing TvShow:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4720 else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
4722 CLog::Log(LOGINFO, "Changing MusicVideo:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4723 content = "musicvideo";
4725 else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
4727 CLog::Log(LOGINFO, "Changing Movie set:id:%i New Title:%s", idMovie, strNewMovieTitle.c_str());
4728 CStdString strSQL = PrepareSQL("UPDATE sets SET strSet='%s' WHERE idSet=%i", strNewMovieTitle.c_str(), idMovie );
4729 m_pDS->exec(strSQL.c_str());
4732 if (!content.empty())
4734 SetSingleValue(iType, idMovie, FieldTitle, strNewMovieTitle);
4735 AnnounceUpdate(content, idMovie);
4740 CLog::Log(LOGERROR, "%s (int idMovie, const CStdString& strNewMovieTitle) failed on MovieID:%i and Title:%s", __FUNCTION__, idMovie, strNewMovieTitle.c_str());
4744 bool CVideoDatabase::UpdateVideoSortTitle(int idDb, const CStdString& strNewSortTitle, VIDEODB_CONTENT_TYPE iType /* = VIDEODB_CONTENT_MOVIES */)
4748 if (NULL == m_pDB.get() || NULL == m_pDS.get())
4750 if (iType != VIDEODB_CONTENT_MOVIES && iType != VIDEODB_CONTENT_TVSHOWS)
4753 CStdString content = "movie";
4754 if (iType == VIDEODB_CONTENT_TVSHOWS)
4757 if (SetSingleValue(iType, idDb, FieldSortTitle, strNewSortTitle))
4759 AnnounceUpdate(content, idDb);
4765 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());
4771 /// \brief EraseVideoSettings() Erases the videoSettings table and reconstructs it
4772 void CVideoDatabase::EraseVideoSettings()
4776 CLog::Log(LOGINFO, "Deleting settings information for all movies");
4777 m_pDS->exec("delete from settings");
4781 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4785 bool CVideoDatabase::GetGenresNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4787 return GetNavCommon(strBaseDir, items, "genre", idContent, filter, countOnly);
4790 bool CVideoDatabase::GetCountriesNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4792 return GetNavCommon(strBaseDir, items, "country", idContent, filter, countOnly);
4795 bool CVideoDatabase::GetStudiosNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4797 return GetNavCommon(strBaseDir, items, "studio", idContent, filter, countOnly);
4800 bool CVideoDatabase::GetNavCommon(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4804 if (NULL == m_pDB.get()) return false;
4805 if (NULL == m_pDS.get()) return false;
4808 Filter extFilter = filter;
4809 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4811 if (idContent == VIDEODB_CONTENT_MOVIES)
4813 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4814 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());
4815 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",
4816 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4818 else if (idContent == VIDEODB_CONTENT_TVSHOWS) //this will not get tvshows with 0 episodes
4820 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4821 extFilter.fields = PrepareSQL("%s.id%s, %s.str%s, path.strPath", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4822 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",
4823 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4825 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4827 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4828 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());
4829 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",
4830 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4837 if (idContent == VIDEODB_CONTENT_MOVIES)
4839 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4840 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());
4841 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",
4842 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4843 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4845 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4847 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4848 extFilter.fields = PrepareSQL("distinct %s.id%s, %s.str%s", type.c_str(), type.c_str(), type.c_str(), type.c_str());
4849 extFilter.AppendJoin(PrepareSQL("join %slinktvshow on %s.id%s = %slinktvshow.id%s join tvshowview on %slinktvshow.idShow = tvshowview.idShow",
4850 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4852 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4854 strSQL = "select %s " + PrepareSQL("from %s ", type.c_str());
4855 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());
4856 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",
4857 type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str(), type.c_str()));
4858 extFilter.AppendGroup(PrepareSQL("%s.id%s", type.c_str(), type.c_str()));
4866 extFilter.fields = PrepareSQL("COUNT(DISTINCT %s.id%s)", type.c_str(), type.c_str());
4867 extFilter.group.clear();
4868 extFilter.order.clear();
4870 strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
4872 CVideoDbUrl videoUrl;
4873 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
4876 int iRowsFound = RunQuery(strSQL);
4877 if (iRowsFound <= 0)
4878 return iRowsFound == 0;
4882 CFileItemPtr pItem(new CFileItem());
4883 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
4890 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
4892 map<int, pair<CStdString,int> > mapItems;
4893 map<int, pair<CStdString,int> >::iterator it;
4894 while (!m_pDS->eof())
4896 int id = m_pDS->fv(0).get_asInt();
4897 CStdString str = m_pDS->fv(1).get_asString();
4899 // was this already found?
4900 it = mapItems.find(id);
4901 if (it == mapItems.end())
4904 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
4906 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4907 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,m_pDS->fv(3).get_asInt()))); //fv(3) is file.playCount
4908 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4909 mapItems.insert(pair<int, pair<CStdString,int> >(id, pair<CStdString,int>(str,0)));
4916 for (it = mapItems.begin(); it != mapItems.end(); ++it)
4918 CFileItemPtr pItem(new CFileItem(it->second.first));
4919 pItem->GetVideoInfoTag()->m_iDbId = it->first;
4920 pItem->GetVideoInfoTag()->m_type = type;
4922 CVideoDbUrl itemUrl = videoUrl;
4923 CStdString path; path.Format("%ld/", it->first);
4924 itemUrl.AppendPath(path);
4925 pItem->SetPath(itemUrl.ToString());
4927 pItem->m_bIsFolder = true;
4928 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4929 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
4930 if (!items.Contains(pItem->GetPath()))
4932 pItem->SetLabelPreformated(true);
4939 while (!m_pDS->eof())
4941 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
4942 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
4943 pItem->GetVideoInfoTag()->m_type = type;
4945 CVideoDbUrl itemUrl = videoUrl;
4946 CStdString path; path.Format("%ld/", m_pDS->fv(0).get_asInt());
4947 itemUrl.AppendPath(path);
4948 pItem->SetPath(itemUrl.ToString());
4950 pItem->m_bIsFolder = true;
4951 pItem->SetLabelPreformated(true);
4952 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4953 { // fv(3) is the number of videos watched, fv(2) is the total number. We set the playcount
4954 // only if the number of videos watched is equal to the total number (i.e. every video watched)
4955 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(3).get_asInt() == m_pDS->fv(2).get_asInt()) ? 1 : 0;
4966 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
4971 bool CVideoDatabase::GetTagsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
4973 CStdString mediaType;
4974 if (idContent == VIDEODB_CONTENT_MOVIES)
4975 mediaType = "movie";
4976 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
4977 mediaType = "tvshow";
4978 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
4979 mediaType = "musicvideo";
4985 if (NULL == m_pDB.get()) return false;
4986 if (NULL == m_pDS.get()) return false;
4988 CStdString strSQL = "SELECT %s FROM taglinks ";
4990 Filter extFilter = filter;
4991 extFilter.fields = "tag.idTag, tag.strTag";
4992 extFilter.AppendJoin("JOIN tag ON tag.idTag = taglinks.idTag");
4994 if (idContent == (int)VIDEODB_CONTENT_MOVIES)
4995 extFilter.AppendJoin("JOIN movieview ON movieview.idMovie = taglinks.idMedia");
4997 extFilter.AppendWhere(PrepareSQL("taglinks.media_type = '%s'", mediaType.c_str()));
4998 extFilter.AppendGroup("taglinks.idTag");
5002 extFilter.fields = "COUNT(DISTINCT taglinks.idTag)";
5003 extFilter.group.clear();
5004 extFilter.order.clear();
5006 strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5008 // parse the base path to get additional filters
5009 CVideoDbUrl videoUrl;
5010 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5013 int iRowsFound = RunQuery(strSQL);
5014 if (iRowsFound <= 0)
5015 return iRowsFound == 0;
5019 CFileItemPtr pItem(new CFileItem());
5020 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5027 while (!m_pDS->eof())
5029 int idTag = m_pDS->fv(0).get_asInt();
5031 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5032 pItem->m_bIsFolder = true;
5033 pItem->GetVideoInfoTag()->m_iDbId = idTag;
5034 pItem->GetVideoInfoTag()->m_type = "tag";
5036 CVideoDbUrl itemUrl = videoUrl;
5037 CStdString path; path.Format("%ld/", idTag);
5038 itemUrl.AppendPath(path);
5039 pItem->SetPath(itemUrl.ToString());
5041 if (!items.Contains(pItem->GetPath()))
5043 pItem->SetLabelPreformated(true);
5055 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5060 bool CVideoDatabase::GetSetsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool ignoreSingleMovieSets /* = false */)
5062 if (idContent != VIDEODB_CONTENT_MOVIES)
5065 return GetSetsByWhere(strBaseDir, filter, items, ignoreSingleMovieSets);
5068 bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets /* = false */)
5072 if (NULL == m_pDB.get()) return false;
5073 if (NULL == m_pDS.get()) return false;
5075 CVideoDbUrl videoUrl;
5076 if (!videoUrl.FromString(strBaseDir))
5079 Filter setFilter = filter;
5080 setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
5081 if (!setFilter.order.empty())
5082 setFilter.order += ",";
5083 setFilter.order += "sets.idSet";
5085 if (!GetMoviesByWhere(strBaseDir, setFilter, items))
5089 if (!GroupUtils::Group(GroupBySet, strBaseDir, items, sets))
5099 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5104 bool CVideoDatabase::GetMusicVideoAlbumsNav(const CStdString& strBaseDir, CFileItemList& items, int idArtist /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5108 if (NULL == m_pDB.get()) return false;
5109 if (NULL == m_pDS.get()) return false;
5111 CStdString strSQL = "select %s from musicvideoview ";
5112 Filter extFilter = filter;
5113 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5115 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor, path.strPath", VIDEODB_ID_MUSICVIDEO_ALBUM);
5116 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");
5120 extFilter.fields = PrepareSQL("musicvideoview.c%02d, musicvideoview.idMVideo, actors.strActor", VIDEODB_ID_MUSICVIDEO_ALBUM);
5121 extFilter.AppendJoin("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist");
5124 extFilter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", idArtist));
5126 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM));
5130 extFilter.fields = "COUNT(1)";
5131 extFilter.group.clear();
5132 extFilter.order.clear();
5134 strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5136 CVideoDbUrl videoUrl;
5137 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5140 int iRowsFound = RunQuery(strSQL);
5141 if (iRowsFound <= 0)
5142 return iRowsFound == 0;
5146 CFileItemPtr pItem(new CFileItem());
5147 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5154 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5156 map<int, pair<CStdString,CStdString> > mapAlbums;
5157 map<int, pair<CStdString,CStdString> >::iterator it;
5158 while (!m_pDS->eof())
5160 int lidMVideo = m_pDS->fv(1).get_asInt();
5161 CStdString strAlbum = m_pDS->fv(0).get_asString();
5162 it = mapAlbums.find(lidMVideo);
5163 // was this genre already found?
5164 if (it == mapAlbums.end())
5167 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5168 mapAlbums.insert(make_pair(lidMVideo, make_pair(strAlbum,m_pDS->fv(2).get_asString())));
5174 for (it=mapAlbums.begin();it != mapAlbums.end();++it)
5176 if (!it->second.first.IsEmpty())
5178 CFileItemPtr pItem(new CFileItem(it->second.first));
5180 CVideoDbUrl itemUrl = videoUrl;
5181 CStdString path; path.Format("%ld/", it->first);
5182 itemUrl.AppendPath(path);
5183 pItem->SetPath(itemUrl.ToString());
5185 pItem->m_bIsFolder=true;
5186 pItem->SetLabelPreformated(true);
5187 if (!items.Contains(pItem->GetPath()))
5189 pItem->GetVideoInfoTag()->m_artist.push_back(it->second.second);
5197 while (!m_pDS->eof())
5199 if (!m_pDS->fv(0).get_asString().empty())
5201 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
5203 CVideoDbUrl itemUrl = videoUrl;
5204 CStdString path; path.Format("%ld/", m_pDS->fv(1).get_asInt());
5205 itemUrl.AppendPath(path);
5206 pItem->SetPath(itemUrl.ToString());
5208 pItem->m_bIsFolder=true;
5209 pItem->SetLabelPreformated(true);
5210 if (!items.Contains(pItem->GetPath()))
5212 pItem->GetVideoInfoTag()->m_artist.push_back(m_pDS->fv(2).get_asString());
5221 // CLog::Log(LOGDEBUG, __FUNCTION__" Time: %d ms", XbmcThreads::SystemClockMillis() - time);
5226 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5231 bool CVideoDatabase::GetWritersNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5233 return GetPeopleNav(strBaseDir, items, "writer", idContent, filter, countOnly);
5236 bool CVideoDatabase::GetDirectorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5238 return GetPeopleNav(strBaseDir, items, "director", idContent, filter, countOnly);
5241 bool CVideoDatabase::GetActorsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5243 if (GetPeopleNav(strBaseDir, items, (idContent == VIDEODB_CONTENT_MUSICVIDEOS) ? "artist" : "actor", idContent, filter, countOnly))
5244 { // set thumbs - ideally this should be in the normal thumb setting routines
5245 for (int i = 0; i < items.Size() && !countOnly; i++)
5247 CFileItemPtr pItem = items[i];
5248 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5249 pItem->SetIconImage("DefaultArtist.png");
5251 pItem->SetIconImage("DefaultActor.png");
5258 bool CVideoDatabase::GetPeopleNav(const CStdString& strBaseDir, CFileItemList& items, const CStdString &type, int idContent /* = -1 */, const Filter &filter /* = Filter() */, bool countOnly /* = false */)
5260 if (NULL == m_pDB.get()) return false;
5261 if (NULL == m_pDS.get()) return false;
5265 // TODO: This routine (and probably others at this same level) use playcount as a reference to filter on at a later
5266 // point. This means that we *MUST* filter these levels as you'll get double ups. Ideally we'd allow playcount
5267 // to filter through as we normally do for tvshows to save this happening.
5268 // Also, we apply this same filtering logic to the locked or unlocked paths to prevent these from showing.
5269 // Whether or not this should happen is a tricky one - it complicates all the high level categories (everything
5272 // General routine that the other actor/director/writer routines call
5274 // get primary genres for movies
5276 Filter extFilter = filter;
5277 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5279 if (idContent == VIDEODB_CONTENT_MOVIES)
5281 strSQL = "select %s from actors ";
5282 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5283 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",
5284 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5286 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5288 strSQL = "select %s from actors ";
5289 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath";
5290 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",
5291 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5293 else if (idContent == VIDEODB_CONTENT_EPISODES)
5295 strSQL = "select %s from actors ";
5296 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5297 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",
5298 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5300 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5302 strSQL = "select %s from actors ";
5303 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, path.strPath, files.playCount";
5304 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",
5305 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5312 if (idContent == VIDEODB_CONTENT_MOVIES)
5314 strSQL ="select %s from actors ";
5315 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5316 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",
5317 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5318 extFilter.AppendGroup("actors.idActor");
5320 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5322 strSQL = "select %s " + PrepareSQL("from actors, %slinktvshow, tvshowview ", type.c_str());
5323 extFilter.fields = "distinct actors.idActor, actors.strActor, actors.strThumb";
5324 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinktvshow.id%s and %slinktvshow.idShow = tvshowview.idShow",
5325 type.c_str(), type.c_str(), type.c_str()));
5327 else if (idContent == VIDEODB_CONTENT_EPISODES)
5329 strSQL = "select %s " + PrepareSQL("from %slinkepisode, actors, episodeview, files ", type.c_str());
5330 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5331 extFilter.AppendWhere(PrepareSQL("actors.idActor = %slinkepisode.id%s and %slinkepisode.idEpisode = episodeview.idEpisode and files.idFile = episodeview.idFile",
5332 type.c_str(), type.c_str(), type.c_str()));
5333 extFilter.AppendGroup("actors.idActor");
5335 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5337 strSQL = "select %s from actors ";
5338 extFilter.fields = "actors.idActor, actors.strActor, actors.strThumb, count(1), count(files.playCount)";
5339 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",
5340 type.c_str(), type.c_str(), type.c_str(), type.c_str()));
5341 extFilter.AppendGroup("actors.idActor");
5349 extFilter.fields = "COUNT(1)";
5350 extFilter.group.clear();
5351 extFilter.order.clear();
5353 strSQL.Format(strSQL.c_str(), !extFilter.fields.empty() ? extFilter.fields.c_str() : "*");
5355 CVideoDbUrl videoUrl;
5356 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5360 unsigned int time = XbmcThreads::SystemClockMillis();
5361 if (!m_pDS->query(strSQL.c_str())) return false;
5362 CLog::Log(LOGDEBUG, "%s - query took %i ms",
5363 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5364 int iRowsFound = m_pDS->num_rows();
5365 if (iRowsFound == 0)
5373 CFileItemPtr pItem(new CFileItem());
5374 pItem->SetProperty("total", iRowsFound == 1 ? m_pDS->fv(0).get_asInt() : iRowsFound);
5381 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5383 map<int, CActor> mapActors;
5384 map<int, CActor>::iterator it;
5386 while (!m_pDS->eof())
5388 int idActor = m_pDS->fv(0).get_asInt();
5390 actor.name = m_pDS->fv(1).get_asString();
5391 actor.thumb = m_pDS->fv(2).get_asString();
5392 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5393 actor.playcount = m_pDS->fv(3).get_asInt();
5394 it = mapActors.find(idActor);
5395 // is this actor already known?
5396 if (it == mapActors.end())
5399 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5400 mapActors.insert(pair<int, CActor>(idActor, actor));
5406 for (it=mapActors.begin();it != mapActors.end();++it)
5408 CFileItemPtr pItem(new CFileItem(it->second.name));
5410 CVideoDbUrl itemUrl = videoUrl;
5411 CStdString path; path.Format("%ld/", it->first);
5412 itemUrl.AppendPath(path);
5413 pItem->SetPath(itemUrl.ToString());
5415 pItem->m_bIsFolder=true;
5416 pItem->GetVideoInfoTag()->m_playCount = it->second.playcount;
5417 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(it->second.thumb);
5418 pItem->GetVideoInfoTag()->m_iDbId = it->first;
5419 pItem->GetVideoInfoTag()->m_type = type;
5425 while (!m_pDS->eof())
5429 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
5431 CVideoDbUrl itemUrl = videoUrl;
5432 CStdString path; path.Format("%ld/", m_pDS->fv(0).get_asInt());
5433 itemUrl.AppendPath(path);
5434 pItem->SetPath(itemUrl.ToString());
5436 pItem->m_bIsFolder=true;
5437 pItem->GetVideoInfoTag()->m_strPictureURL.ParseString(m_pDS->fv(2).get_asString());
5438 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(0).get_asInt();
5439 pItem->GetVideoInfoTag()->m_type = type;
5440 if (idContent != VIDEODB_CONTENT_TVSHOWS)
5442 // fv(4) is the number of videos watched, fv(3) is the total number. We set the playcount
5443 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5444 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(4).get_asInt() == m_pDS->fv(3).get_asInt()) ? 1 : 0;
5446 if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5447 pItem->GetVideoInfoTag()->m_artist.push_back(pItem->GetLabel());
5454 CLog::Log(LOGERROR, "%s: out of memory - retrieved %i items", __FUNCTION__, items.Size());
5455 return items.Size() > 0;
5460 CLog::Log(LOGDEBUG, "%s item retrieval took %i ms",
5461 __FUNCTION__, XbmcThreads::SystemClockMillis() - time); time = XbmcThreads::SystemClockMillis();
5468 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5473 bool CVideoDatabase::GetYearsNav(const CStdString& strBaseDir, CFileItemList& items, int idContent /* = -1 */, const Filter &filter /* = Filter() */)
5477 if (NULL == m_pDB.get()) return false;
5478 if (NULL == m_pDS.get()) return false;
5481 Filter extFilter = filter;
5482 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5484 if (idContent == VIDEODB_CONTENT_MOVIES)
5486 strSQL = PrepareSQL("select movieview.c%02d, path.strPath, files.playCount from movieview ", VIDEODB_ID_YEAR);
5487 extFilter.AppendJoin("join files on files.idFile = movieview.idFile join path on files.idPath = path.idPath");
5489 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5491 strSQL = PrepareSQL("select tvshowview.c%02d, path.strPath from tvshowview ", VIDEODB_ID_TV_PREMIERED);
5492 extFilter.AppendJoin("join episodeview on episodeview.idShow = tvshowview.idShow join files on files.idFile = episodeview.idFile join path on files.idPath = path.idPath");
5494 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5496 strSQL = PrepareSQL("select musicvideoview.c%02d, path.strPath, files.playCount from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5497 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile join path on files.idPath = path.idPath");
5505 if (idContent == VIDEODB_CONTENT_MOVIES)
5507 strSQL = PrepareSQL("select movieview.c%02d, count(1), count(files.playCount) from movieview ", VIDEODB_ID_YEAR);
5508 extFilter.AppendJoin("join files on files.idFile = movieview.idFile");
5509 extFilter.AppendGroup(PrepareSQL("movieview.c%02d", VIDEODB_ID_YEAR));
5511 else if (idContent == VIDEODB_CONTENT_TVSHOWS)
5512 strSQL = PrepareSQL("select distinct tvshowview.c%02d from tvshowview", VIDEODB_ID_TV_PREMIERED);
5513 else if (idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5515 strSQL = PrepareSQL("select musicvideoview.c%02d, count(1), count(files.playCount) from musicvideoview ", VIDEODB_ID_MUSICVIDEO_YEAR);
5516 extFilter.AppendJoin("join files on files.idFile = musicvideoview.idFile");
5517 extFilter.AppendGroup(PrepareSQL("musicvideoview.c%02d", VIDEODB_ID_MUSICVIDEO_YEAR));
5523 CVideoDbUrl videoUrl;
5524 if (!BuildSQL(strBaseDir, strSQL, extFilter, strSQL, videoUrl))
5527 int iRowsFound = RunQuery(strSQL);
5528 if (iRowsFound <= 0)
5529 return iRowsFound == 0;
5531 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5533 map<int, pair<CStdString,int> > mapYears;
5534 map<int, pair<CStdString,int> >::iterator it;
5535 while (!m_pDS->eof())
5538 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5541 time.SetFromDateString(m_pDS->fv(0).get_asString());
5542 lYear = time.GetYear();
5544 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5545 lYear = m_pDS->fv(0).get_asInt();
5546 it = mapYears.find(lYear);
5547 if (it == mapYears.end())
5550 if (g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5553 year.Format("%d", lYear);
5554 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5555 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,m_pDS->fv(2).get_asInt())));
5557 mapYears.insert(pair<int, pair<CStdString,int> >(lYear, pair<CStdString,int>(year,0)));
5564 for (it=mapYears.begin();it != mapYears.end();++it)
5568 CFileItemPtr pItem(new CFileItem(it->second.first));
5570 CVideoDbUrl itemUrl = videoUrl;
5571 CStdString path; path.Format("%ld/", it->first);
5572 itemUrl.AppendPath(path);
5573 pItem->SetPath(itemUrl.ToString());
5575 pItem->m_bIsFolder=true;
5576 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5577 pItem->GetVideoInfoTag()->m_playCount = it->second.second;
5583 while (!m_pDS->eof())
5586 CStdString strLabel;
5587 if (idContent == VIDEODB_CONTENT_TVSHOWS)
5590 time.SetFromDateString(m_pDS->fv(0).get_asString());
5591 lYear = time.GetYear();
5592 strLabel.Format("%i",lYear);
5594 else if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5596 lYear = m_pDS->fv(0).get_asInt();
5597 strLabel = m_pDS->fv(0).get_asString();
5604 CFileItemPtr pItem(new CFileItem(strLabel));
5606 CVideoDbUrl itemUrl = videoUrl;
5607 CStdString path; path.Format("%ld/", lYear);
5608 itemUrl.AppendPath(path);
5609 pItem->SetPath(itemUrl.ToString());
5611 pItem->m_bIsFolder=true;
5612 if (idContent == VIDEODB_CONTENT_MOVIES || idContent == VIDEODB_CONTENT_MUSICVIDEOS)
5614 // fv(2) is the number of videos watched, fv(1) is the total number. We set the playcount
5615 // only if the number of videos watched is equal to the total number (i.e. every video watched)
5616 pItem->GetVideoInfoTag()->m_playCount = (m_pDS->fv(2).get_asInt() == m_pDS->fv(1).get_asInt()) ? 1 : 0;
5619 // take care of dupes ..
5620 if (!items.Contains(pItem->GetPath()))
5632 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5637 bool CVideoDatabase::GetStackedTvShowList(int idShow, CStdString& strIn) const
5641 if (NULL == m_pDB.get()) return false;
5642 if (NULL == m_pDS.get()) return false;
5644 // look for duplicate show titles and stack them into a list
5647 CStdString strSQL = PrepareSQL("select idShow from tvshow where c00 like (select c00 from tvshow where idShow=%i) order by idShow", idShow);
5648 CLog::Log(LOGDEBUG, "%s query: %s", __FUNCTION__, strSQL.c_str());
5649 if (!m_pDS->query(strSQL.c_str())) return false;
5650 int iRows = m_pDS->num_rows();
5651 if (iRows == 0) return false; // this should never happen!
5653 { // more than one show, so stack them up
5655 while (!m_pDS->eof())
5657 strIn += PrepareSQL("%i,", m_pDS->fv(0).get_asInt());
5660 strIn[strIn.GetLength() - 1] = ')'; // replace last , with )
5667 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5672 bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList& items, int idActor, int idDirector, int idGenre, int idYear, int idShow, bool getLinkedMovies /* = true */)
5676 if (NULL == m_pDB.get()) return false;
5677 if (NULL == m_pDS.get()) return false;
5679 // parse the base path to get additional filters
5680 CVideoDbUrl videoUrl;
5681 if (!videoUrl.FromString(strBaseDir))
5684 CStdString strIn = PrepareSQL("= %i", idShow);
5685 GetStackedTvShowList(idShow, strIn);
5687 CStdString strSQL = PrepareSQL("SELECT episodeview.c%02d, "
5689 "tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, tvshowview.c%02d, "
5690 "seasons.idSeason, "
5691 "count(1), count(files.playCount) "
5692 "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);
5695 filter.join = PrepareSQL("JOIN tvshowview ON tvshowview.idShow = episodeview.idShow "
5696 "JOIN seasons ON (seasons.idShow = tvshowview.idShow AND seasons.season = episodeview.c%02d) "
5697 "JOIN files ON files.idFile = episodeview.idFile "
5698 "JOIN tvshowlinkpath ON tvshowlinkpath.idShow = tvshowview.idShow "
5699 "JOIN path ON path.idPath = tvshowlinkpath.idPath", VIDEODB_ID_EPISODE_SEASON);
5700 filter.where = PrepareSQL("tvshowview.idShow %s", strIn.c_str());
5701 filter.group = PrepareSQL("episodeview.c%02d", VIDEODB_ID_EPISODE_SEASON);
5703 videoUrl.AddOption("tvshowid", idShow);
5706 videoUrl.AddOption("actorid", idActor);
5707 else if (idDirector != -1)
5708 videoUrl.AddOption("directorid", idDirector);
5709 else if (idGenre != -1)
5710 videoUrl.AddOption("genreid", idGenre);
5711 else if (idYear != -1)
5712 videoUrl.AddOption("year", idYear);
5714 if (!BuildSQL(strBaseDir, strSQL, filter, strSQL, videoUrl))
5717 int iRowsFound = RunQuery(strSQL);
5718 if (iRowsFound <= 0)
5719 return iRowsFound == 0;
5721 // show titles, plots, day of premiere, studios and mpaa ratings will be the same
5722 CStdString showTitle = m_pDS->fv(2).get_asString();
5723 CStdString showPlot = m_pDS->fv(3).get_asString();
5724 CStdString showPremiered = m_pDS->fv(4).get_asString();
5725 CStdString showStudio = m_pDS->fv(6).get_asString();
5726 CStdString showMPAARating = m_pDS->fv(7).get_asString();
5728 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
5730 map<int, CSeason> mapSeasons;
5731 map<int, CSeason>::iterator it;
5732 while (!m_pDS->eof())
5734 int iSeason = m_pDS->fv(0).get_asInt();
5735 it = mapSeasons.find(iSeason);
5737 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
5742 if (it == mapSeasons.end())
5745 season.path = m_pDS->fv(1).get_asString();
5746 season.genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5747 season.id = m_pDS->fv(8).get_asInt();
5748 season.numEpisodes = m_pDS->fv(9).get_asInt();
5749 season.numWatched = m_pDS->fv(10).get_asInt();
5750 mapSeasons.insert(make_pair(iSeason, season));
5756 for (it=mapSeasons.begin();it != mapSeasons.end();++it)
5758 int iSeason = it->first;
5759 CStdString strLabel;
5761 strLabel = g_localizeStrings.Get(20381);
5763 strLabel.Format(g_localizeStrings.Get(20358),iSeason);
5764 CFileItemPtr pItem(new CFileItem(strLabel));
5766 CVideoDbUrl itemUrl = videoUrl;
5767 CStdString strDir; strDir.Format("%ld/", it->first);
5768 itemUrl.AppendPath(strDir);
5769 pItem->SetPath(itemUrl.ToString());
5771 pItem->m_bIsFolder=true;
5772 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5773 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5774 pItem->GetVideoInfoTag()->m_iDbId = it->second.id;
5775 pItem->GetVideoInfoTag()->m_type = "season";
5776 pItem->GetVideoInfoTag()->m_strPath = it->second.path;
5777 pItem->GetVideoInfoTag()->m_genre = it->second.genre;
5778 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5779 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5780 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5781 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5782 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5783 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5784 pItem->GetVideoInfoTag()->m_iEpisode = it->second.numEpisodes;
5785 pItem->SetProperty("totalepisodes", it->second.numEpisodes);
5786 pItem->SetProperty("numepisodes", it->second.numEpisodes); // will be changed later to reflect watchmode setting
5787 pItem->SetProperty("watchedepisodes", it->second.numWatched);
5788 pItem->SetProperty("unwatchedepisodes", it->second.numEpisodes - it->second.numWatched);
5789 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5790 pItem->GetVideoInfoTag()->m_playCount = (it->second.numEpisodes == it->second.numWatched) ? 1 : 0;
5791 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5797 while (!m_pDS->eof())
5799 int iSeason = m_pDS->fv(0).get_asInt();
5800 CStdString strLabel;
5802 strLabel = g_localizeStrings.Get(20381);
5804 strLabel.Format(g_localizeStrings.Get(20358),iSeason);
5805 CFileItemPtr pItem(new CFileItem(strLabel));
5807 CVideoDbUrl itemUrl = videoUrl;
5808 CStdString strDir; strDir.Format("%ld/", iSeason);
5809 itemUrl.AppendPath(strDir);
5810 pItem->SetPath(itemUrl.ToString());
5812 pItem->m_bIsFolder=true;
5813 pItem->GetVideoInfoTag()->m_strTitle = strLabel;
5814 pItem->GetVideoInfoTag()->m_iSeason = iSeason;
5815 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv(8).get_asInt();
5816 pItem->GetVideoInfoTag()->m_type = "season";
5817 pItem->GetVideoInfoTag()->m_strPath = m_pDS->fv(1).get_asString();
5818 pItem->GetVideoInfoTag()->m_genre = StringUtils::Split(m_pDS->fv(5).get_asString(), g_advancedSettings.m_videoItemSeparator);
5819 pItem->GetVideoInfoTag()->m_studio = StringUtils::Split(showStudio, g_advancedSettings.m_videoItemSeparator);
5820 pItem->GetVideoInfoTag()->m_strMPAARating = showMPAARating;
5821 pItem->GetVideoInfoTag()->m_iIdShow = idShow;
5822 pItem->GetVideoInfoTag()->m_strShowTitle = showTitle;
5823 pItem->GetVideoInfoTag()->m_strPlot = showPlot;
5824 pItem->GetVideoInfoTag()->m_premiered.SetFromDBDate(showPremiered);
5825 int totalEpisodes = m_pDS->fv(9).get_asInt();
5826 int watchedEpisodes = m_pDS->fv(10).get_asInt();
5827 pItem->GetVideoInfoTag()->m_iEpisode = totalEpisodes;
5828 pItem->SetProperty("totalepisodes", totalEpisodes);
5829 pItem->SetProperty("numepisodes", totalEpisodes); // will be changed later to reflect watchmode setting
5830 pItem->SetProperty("watchedepisodes", watchedEpisodes);
5831 pItem->SetProperty("unwatchedepisodes", totalEpisodes - watchedEpisodes);
5832 if (iSeason == 0) pItem->SetProperty("isspecial", true);
5833 pItem->GetVideoInfoTag()->m_playCount = (totalEpisodes == watchedEpisodes) ? 1 : 0;
5834 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
5841 // now add any linked movies
5842 if (getLinkedMovies)
5845 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
5846 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
5847 CFileItemList movieItems;
5848 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
5850 if (movieItems.Size() > 0)
5851 items.Append(movieItems);
5858 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
5863 bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5865 if (NULL == m_pDB.get() || NULL == m_pDS.get())
5868 if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
5871 SortDescription sorting = sortDescription;
5872 if (sortDescription.sortBy == SortByFile ||
5873 sortDescription.sortBy == SortByTitle ||
5874 sortDescription.sortBy == SortBySortTitle ||
5875 sortDescription.sortBy == SortByLabel ||
5876 sortDescription.sortBy == SortByDateAdded ||
5877 sortDescription.sortBy == SortByRating ||
5878 sortDescription.sortBy == SortByYear ||
5879 sortDescription.sortBy == SortByLastPlayed ||
5880 sortDescription.sortBy == SortByPlaycount)
5881 sorting.sortAttributes = (SortAttribute)(sortDescription.sortAttributes | SortAttributeIgnoreFolders);
5883 bool success = false;
5886 case MediaTypeMovie:
5887 success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5890 case MediaTypeTvShow:
5891 success = GetTvShowsByWhere(strBaseDir, filter, items, sorting);
5894 case MediaTypeEpisode:
5895 success = GetEpisodesByWhere(strBaseDir, filter, items, true, sorting);
5898 case MediaTypeMusicVideo:
5899 success = GetMusicVideosByWhere(strBaseDir, filter, items, true, sorting);
5906 items.SetContent(DatabaseUtils::MediaTypeToString(mediaType) + "s");
5910 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5912 CVideoDbUrl videoUrl;
5913 if (!videoUrl.FromString(strBaseDir))
5916 return GetItems(strBaseDir, videoUrl.GetType(), videoUrl.GetItemType(), items, filter, sortDescription);
5919 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, const CStdString &mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5921 VIDEODB_CONTENT_TYPE contentType;
5922 if (mediaType.Equals("movies"))
5923 contentType = VIDEODB_CONTENT_MOVIES;
5924 else if (mediaType.Equals("tvshows"))
5926 if (itemType.Equals("episodes"))
5927 contentType = VIDEODB_CONTENT_EPISODES;
5929 contentType = VIDEODB_CONTENT_TVSHOWS;
5931 else if (mediaType.Equals("musicvideos"))
5932 contentType = VIDEODB_CONTENT_MUSICVIDEOS;
5936 return GetItems(strBaseDir, contentType, itemType, items, filter, sortDescription);
5939 bool CVideoDatabase::GetItems(const CStdString &strBaseDir, VIDEODB_CONTENT_TYPE mediaType, const CStdString &itemType, CFileItemList &items, const Filter &filter /* = Filter() */, const SortDescription &sortDescription /* = SortDescription() */)
5941 if (itemType.Equals("movies") && (mediaType == VIDEODB_CONTENT_MOVIES || mediaType == VIDEODB_CONTENT_MOVIE_SETS))
5942 return GetMoviesByWhere(strBaseDir, filter, items, sortDescription);
5943 else if (itemType.Equals("tvshows") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5944 return GetTvShowsByWhere(strBaseDir, filter, items, sortDescription);
5945 else if (itemType.Equals("musicvideos") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5946 return GetMusicVideosByWhere(strBaseDir, filter, items, true, sortDescription);
5947 else if (itemType.Equals("episodes") && mediaType == VIDEODB_CONTENT_EPISODES)
5948 return GetEpisodesByWhere(strBaseDir, filter, items, true, sortDescription);
5949 else if (itemType.Equals("seasons") && mediaType == VIDEODB_CONTENT_TVSHOWS)
5950 return GetSeasonsNav(strBaseDir, items);
5951 else if (itemType.Equals("genres"))
5952 return GetGenresNav(strBaseDir, items, mediaType, filter);
5953 else if (itemType.Equals("years"))
5954 return GetYearsNav(strBaseDir, items, mediaType, filter);
5955 else if (itemType.Equals("actors"))
5956 return GetActorsNav(strBaseDir, items, mediaType, filter);
5957 else if (itemType.Equals("directors"))
5958 return GetDirectorsNav(strBaseDir, items, mediaType, filter);
5959 else if (itemType.Equals("writers"))
5960 return GetWritersNav(strBaseDir, items, mediaType, filter);
5961 else if (itemType.Equals("studios"))
5962 return GetStudiosNav(strBaseDir, items, mediaType, filter);
5963 else if (itemType.Equals("sets"))
5964 return GetSetsNav(strBaseDir, items, mediaType, filter);
5965 else if (itemType.Equals("countries"))
5966 return GetCountriesNav(strBaseDir, items, mediaType, filter);
5967 else if (itemType.Equals("tags"))
5968 return GetTagsNav(strBaseDir, items, mediaType, filter);
5969 else if (itemType.Equals("artists") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5970 return GetActorsNav(strBaseDir, items, mediaType, filter);
5971 else if (itemType.Equals("albums") && mediaType == VIDEODB_CONTENT_MUSICVIDEOS)
5972 return GetMusicVideoAlbumsNav(strBaseDir, items, -1, filter);
5977 CStdString CVideoDatabase::GetItemById(const CStdString &itemType, int id)
5979 if (itemType.Equals("genres"))
5980 return GetGenreById(id);
5981 else if (itemType.Equals("years"))
5983 CStdString tmp; tmp.Format("%d", id);
5986 else if (itemType.Equals("actors") || itemType.Equals("directors") || itemType.Equals("artists"))
5987 return GetPersonById(id);
5988 else if (itemType.Equals("studios"))
5989 return GetStudioById(id);
5990 else if (itemType.Equals("sets"))
5991 return GetSetById(id);
5992 else if (itemType.Equals("countries"))
5993 return GetCountryById(id);
5994 else if (itemType.Equals("tags"))
5995 return GetTagById(id);
5996 else if (itemType.Equals("albums"))
5997 return GetMusicVideoAlbumById(id);
6002 bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& items,
6003 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */,
6004 int idStudio /* = -1 */, int idCountry /* = -1 */, int idSet /* = -1 */, int idTag /* = -1 */,
6005 const SortDescription &sortDescription /* = SortDescription() */)
6007 CVideoDbUrl videoUrl;
6008 if (!videoUrl.FromString(strBaseDir))
6012 videoUrl.AddOption("genreid", idGenre);
6013 else if (idCountry > 0)
6014 videoUrl.AddOption("countryid", idCountry);
6015 else if (idStudio > 0)
6016 videoUrl.AddOption("studioid", idStudio);
6017 else if (idDirector > 0)
6018 videoUrl.AddOption("directorid", idDirector);
6019 else if (idYear > 0)
6020 videoUrl.AddOption("year", idYear);
6021 else if (idActor > 0)
6022 videoUrl.AddOption("actorid", idActor);
6024 videoUrl.AddOption("setid", idSet);
6026 videoUrl.AddOption("tagid", idTag);
6029 return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
6032 bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
6039 if (NULL == m_pDB.get()) return false;
6040 if (NULL == m_pDS.get()) return false;
6042 // parse the base path to get additional filters
6043 CVideoDbUrl videoUrl;
6044 Filter extFilter = filter;
6045 SortDescription sorting = sortDescription;
6046 if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
6051 CStdString strSQL = "select %s from movieview ";
6052 CStdString strSQLExtra;
6053 if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
6056 // Apply the limiting directly here if there's no special sorting but limiting
6057 if (extFilter.limit.empty() &&
6058 sorting.sortBy == SortByNone &&
6059 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6061 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6062 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6065 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6067 int iRowsFound = RunQuery(strSQL);
6068 if (iRowsFound <= 0)
6069 return iRowsFound == 0;
6071 // store the total value of items as a property
6072 if (total < iRowsFound)
6074 items.SetProperty("total", total);
6076 DatabaseResults results;
6077 results.reserve(iRowsFound);
6079 if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
6082 // get data from returned rows
6083 items.Reserve(results.size());
6084 const query_data &data = m_pDS->get_result_set().records;
6085 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6087 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6088 const dbiplus::sql_record* const record = data.at(targetRow);
6090 CVideoInfoTag movie = GetDetailsForMovie(record);
6091 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6092 g_passwordManager.bMasterUser ||
6093 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6095 CFileItemPtr pItem(new CFileItem(movie));
6097 CVideoDbUrl itemUrl = videoUrl;
6098 CStdString path; path.Format("%ld", movie.m_iDbId);
6099 itemUrl.AppendPath(path);
6100 pItem->SetPath(itemUrl.ToString());
6102 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED,movie.m_playCount > 0);
6113 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6118 bool CVideoDatabase::GetTvShowsNav(const CStdString& strBaseDir, CFileItemList& items,
6119 int idGenre /* = -1 */, int idYear /* = -1 */, int idActor /* = -1 */, int idDirector /* = -1 */, int idStudio /* = -1 */, int idTag /* = -1 */,
6120 const SortDescription &sortDescription /* = SortDescription() */)
6122 CVideoDbUrl videoUrl;
6123 if (!videoUrl.FromString(strBaseDir))
6127 videoUrl.AddOption("genreid", idGenre);
6128 else if (idStudio != -1)
6129 videoUrl.AddOption("studioid", idStudio);
6130 else if (idDirector != -1)
6131 videoUrl.AddOption("directorid", idDirector);
6132 else if (idYear != -1)
6133 videoUrl.AddOption("year", idYear);
6134 else if (idActor != -1)
6135 videoUrl.AddOption("actorid", idActor);
6136 else if (idTag != -1)
6137 videoUrl.AddOption("tagid", idTag);
6140 return GetTvShowsByWhere(videoUrl.ToString(), filter, items, sortDescription);
6143 bool CVideoDatabase::GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
6149 if (NULL == m_pDB.get()) return false;
6150 if (NULL == m_pDS.get()) return false;
6154 CStdString strSQL = "SELECT %s FROM tvshowview ";
6155 CVideoDbUrl videoUrl;
6156 CStdString strSQLExtra;
6157 Filter extFilter = filter;
6158 SortDescription sorting = sortDescription;
6159 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6162 // Apply the limiting directly here if there's no special sorting but limiting
6163 if (extFilter.limit.empty() &&
6164 sorting.sortBy == SortByNone &&
6165 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6167 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6168 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6171 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6173 int iRowsFound = RunQuery(strSQL);
6174 if (iRowsFound <= 0)
6175 return iRowsFound == 0;
6177 // store the total value of items as a property
6178 if (total < iRowsFound)
6180 items.SetProperty("total", total);
6182 DatabaseResults results;
6183 results.reserve(iRowsFound);
6184 if (!SortUtils::SortFromDataset(sorting, MediaTypeTvShow, m_pDS, results))
6187 // get data from returned rows
6188 items.Reserve(results.size());
6189 const query_data &data = m_pDS->get_result_set().records;
6190 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6192 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6193 const dbiplus::sql_record* const record = data.at(targetRow);
6195 CVideoInfoTag movie = GetDetailsForTvShow(record, false);
6196 if ((CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6197 g_passwordManager.bMasterUser ||
6198 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video"))) &&
6199 (!g_advancedSettings.m_bVideoLibraryHideEmptySeries || movie.m_iEpisode > 0))
6201 CFileItemPtr pItem(new CFileItem(movie));
6203 CVideoDbUrl itemUrl = videoUrl;
6204 CStdString path; path.Format("%ld/", record->at(0).get_asInt());
6205 itemUrl.AppendPath(path);
6206 pItem->SetPath(itemUrl.ToString());
6208 pItem->m_dateTime = movie.m_premiered;
6209 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6210 pItem->SetProperty("totalseasons", record->at(VIDEODB_DETAILS_TVSHOW_NUM_SEASONS).get_asInt());
6211 pItem->SetProperty("totalepisodes", movie.m_iEpisode);
6212 pItem->SetProperty("numepisodes", movie.m_iEpisode); // will be changed later to reflect watchmode setting
6213 pItem->SetProperty("watchedepisodes", movie.m_playCount);
6214 pItem->SetProperty("unwatchedepisodes", movie.m_iEpisode - movie.m_playCount);
6215 pItem->GetVideoInfoTag()->m_playCount = (movie.m_iEpisode == movie.m_playCount) ? 1 : 0;
6216 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6221 Stack(items, VIDEODB_CONTENT_TVSHOWS, !filter.order.empty() || sorting.sortBy != SortByNone);
6229 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6234 void CVideoDatabase::Stack(CFileItemList& items, VIDEODB_CONTENT_TYPE type, bool maintainSortOrder /* = false */)
6236 if (maintainSortOrder)
6238 // save current sort order
6239 for (int i = 0; i < items.Size(); i++)
6240 items[i]->m_iprogramCount = i;
6245 case VIDEODB_CONTENT_TVSHOWS:
6248 items.Sort(SortBySortTitle, SortOrderAscending);
6251 while (i < items.Size())
6253 CFileItemPtr pItem = items.Get(i);
6254 CStdString strTitle = pItem->GetVideoInfoTag()->m_strTitle;
6255 CStdString strFanArt = pItem->GetArt("fanart");
6258 bool bStacked = false;
6259 while (j < items.Size())
6261 CFileItemPtr jItem = items.Get(j);
6263 // matching title? append information
6264 if (jItem->GetVideoInfoTag()->m_strTitle.Equals(strTitle))
6266 if (jItem->GetVideoInfoTag()->m_premiered !=
6267 pItem->GetVideoInfoTag()->m_premiered)
6274 // increment episode counts
6275 pItem->GetVideoInfoTag()->m_iEpisode += jItem->GetVideoInfoTag()->m_iEpisode;
6276 pItem->IncrementProperty("totalepisodes", (int)jItem->GetProperty("totalepisodes").asInteger());
6277 pItem->IncrementProperty("numepisodes", (int)jItem->GetProperty("numepisodes").asInteger()); // will be changed later to reflect watchmode setting
6278 pItem->IncrementProperty("watchedepisodes", (int)jItem->GetProperty("watchedepisodes").asInteger());
6279 pItem->IncrementProperty("unwatchedepisodes", (int)jItem->GetProperty("unwatchedepisodes").asInteger());
6281 // adjust lastplayed
6282 if (jItem->GetVideoInfoTag()->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
6283 pItem->GetVideoInfoTag()->m_lastPlayed = jItem->GetVideoInfoTag()->m_lastPlayed;
6285 // check for fanart if not already set
6286 if (strFanArt.IsEmpty())
6287 strFanArt = jItem->GetArt("fanart");
6289 // remove duplicate entry
6292 // no match? exit loop
6296 // update playcount and fanart
6299 pItem->GetVideoInfoTag()->m_playCount = (pItem->GetVideoInfoTag()->m_iEpisode == (int)pItem->GetProperty("watchedepisodes").asInteger()) ? 1 : 0;
6300 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6301 if (!strFanArt.IsEmpty())
6302 pItem->SetArt("fanart", strFanArt);
6304 // increment i to j which is the next item
6309 // We currently don't stack episodes (No call here in GetEpisodesByWhere()), but this code is left
6310 // so that if we eventually want to stack during scan we can utilize it.
6312 case VIDEODB_CONTENT_EPISODES:
6314 // sort by ShowTitle, Episode, Filename
6315 items.Sort(SortByEpisodeNumber, SortOrderAscending);
6318 while (i < items.Size())
6320 CFileItemPtr pItem = items.Get(i);
6321 CStdString strPath = pItem->GetVideoInfoTag()->m_strPath;
6322 int iSeason = pItem->GetVideoInfoTag()->m_iSeason;
6323 int iEpisode = pItem->GetVideoInfoTag()->m_iEpisode;
6324 //CStdString strFanArt = pItem->GetArt("fanart");
6326 // do we have a dvd folder, ie foo/VIDEO_TS.IFO or foo/VIDEO_TS/VIDEO_TS.IFO
6327 CStdString strFileNameAndPath = pItem->GetVideoInfoTag()->m_strFileNameAndPath;
6328 bool bDvdFolder = strFileNameAndPath.Right(12).Equals("VIDEO_TS.IFO");
6330 vector<CStdString> paths;
6331 paths.push_back(strFileNameAndPath);
6332 CLog::Log(LOGDEBUG, "Stack episode (%i,%i):[%s]", iSeason, iEpisode, paths[0].c_str());
6335 int iPlayCount = pItem->GetVideoInfoTag()->m_playCount;
6336 while (j < items.Size())
6338 CFileItemPtr jItem = items.Get(j);
6339 const CVideoInfoTag *jTag = jItem->GetVideoInfoTag();
6340 CStdString jFileNameAndPath = jTag->m_strFileNameAndPath;
6342 CLog::Log(LOGDEBUG, " *testing (%i,%i):[%s]", jTag->m_iSeason, jTag->m_iEpisode, jFileNameAndPath.c_str());
6343 // compare path, season, episode
6346 jTag->m_strPath.Equals(strPath) &&
6347 jTag->m_iSeason == iSeason &&
6348 jTag->m_iEpisode == iEpisode
6351 // keep checking to see if this is dvd folder
6354 bDvdFolder = jFileNameAndPath.Right(12).Equals("VIDEO_TS.IFO");
6355 // if we have a dvd folder, we stack differently
6358 // remove all the other items and ONLY show the VIDEO_TS.IFO file
6360 paths.push_back(jFileNameAndPath);
6364 // increment playcount
6365 iPlayCount += jTag->m_playCount;
6367 // episodes dont have fanart yet
6368 //if (strFanArt.IsEmpty())
6369 // strFanArt = jItem->GetArt("fanart");
6371 paths.push_back(jFileNameAndPath);
6375 // remove duplicate entry
6379 // no match? exit loop
6383 // update playcount and fanart if we have a stacked entry
6384 if (paths.size() > 1)
6386 CStackDirectory dir;
6387 CStdString strStack;
6388 dir.ConstructStackPath(paths, strStack);
6389 pItem->GetVideoInfoTag()->m_strFileNameAndPath = strStack;
6390 pItem->GetVideoInfoTag()->m_playCount = iPlayCount;
6391 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, (pItem->GetVideoInfoTag()->m_playCount > 0) && (pItem->GetVideoInfoTag()->m_iEpisode > 0));
6393 // episodes dont have fanart yet
6394 //if (!strFanArt.IsEmpty())
6395 // pItem->SetArt("fanart", strFanArt);
6397 // increment i to j which is the next item
6403 // stack other types later
6407 if (maintainSortOrder)
6409 // restore original sort order - essential for smartplaylists
6410 items.Sort(SortByProgramCount, SortOrderAscending);
6414 bool CVideoDatabase::GetEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, int idGenre, int idYear, int idActor, int idDirector, int idShow, int idSeason, const SortDescription &sortDescription /* = SortDescription() */)
6416 CVideoDbUrl videoUrl;
6417 if (!videoUrl.FromString(strBaseDir))
6423 strIn = PrepareSQL("= %i", idShow);
6424 GetStackedTvShowList(idShow, strIn);
6426 videoUrl.AddOption("tvshowid", idShow);
6428 videoUrl.AddOption("season", idSeason);
6431 videoUrl.AddOption("genreid", idGenre);
6432 else if (idYear !=-1)
6433 videoUrl.AddOption("year", idYear);
6434 else if (idActor != -1)
6435 videoUrl.AddOption("actorid", idActor);
6437 else if (idYear != -1)
6438 videoUrl.AddOption("year", idYear);
6440 if (idDirector != -1)
6441 videoUrl.AddOption("directorid", idDirector);
6444 bool ret = GetEpisodesByWhere(videoUrl.ToString(), filter, items, false, sortDescription);
6446 if (idSeason == -1 && idShow != -1)
6447 { // add any linked movies
6449 movieFilter.join = PrepareSQL("join movielinktvshow on movielinktvshow.idMovie=movieview.idMovie");
6450 movieFilter.where = PrepareSQL("movielinktvshow.idShow %s", strIn.c_str());
6451 CFileItemList movieItems;
6452 GetMoviesByWhere("videodb://movies/titles/", movieFilter, movieItems);
6454 if (movieItems.Size() > 0)
6455 items.Append(movieItems);
6461 bool CVideoDatabase::GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath /* = true */, const SortDescription &sortDescription /* = SortDescription() */)
6468 if (NULL == m_pDB.get()) return false;
6469 if (NULL == m_pDS.get()) return false;
6473 CStdString strSQL = "select %s from episodeview ";
6474 CVideoDbUrl videoUrl;
6475 CStdString strSQLExtra;
6476 Filter extFilter = filter;
6477 SortDescription sorting = sortDescription;
6478 if (!BuildSQL(strBaseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
6481 // Apply the limiting directly here if there's no special sorting but limiting
6482 if (extFilter.limit.empty() &&
6483 sorting.sortBy == SortByNone &&
6484 (sorting.limitStart > 0 || sorting.limitEnd > 0))
6486 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
6487 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
6490 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
6492 int iRowsFound = RunQuery(strSQL);
6493 if (iRowsFound <= 0)
6494 return iRowsFound == 0;
6496 // store the total value of items as a property
6497 if (total < iRowsFound)
6499 items.SetProperty("total", total);
6501 DatabaseResults results;
6502 results.reserve(iRowsFound);
6503 if (!SortUtils::SortFromDataset(sorting, MediaTypeEpisode, m_pDS, results))
6506 // get data from returned rows
6507 items.Reserve(results.size());
6508 CLabelFormatter formatter("%H. %T", "");
6510 const query_data &data = m_pDS->get_result_set().records;
6511 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
6513 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
6514 const dbiplus::sql_record* const record = data.at(targetRow);
6516 CVideoInfoTag movie = GetDetailsForEpisode(record);
6517 if (CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE ||
6518 g_passwordManager.bMasterUser ||
6519 g_passwordManager.IsDatabasePathUnlocked(movie.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
6521 CFileItemPtr pItem(new CFileItem(movie));
6522 formatter.FormatLabel(pItem.get());
6524 int idEpisode = record->at(0).get_asInt();
6526 CVideoDbUrl itemUrl = videoUrl;
6528 if (appendFullShowPath && videoUrl.GetItemType() != "episodes")
6529 path.Format("%ld/%ld/%ld", record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt(), movie.m_iSeason, idEpisode);
6531 path.Format("%ld", idEpisode);
6532 itemUrl.AppendPath(path);
6533 pItem->SetPath(itemUrl.ToString());
6535 pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, movie.m_playCount > 0);
6536 pItem->m_dateTime = movie.m_firstAired;
6537 pItem->GetVideoInfoTag()->m_iYear = pItem->m_dateTime.GetYear();
6548 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6553 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() */)
6555 CVideoDbUrl videoUrl;
6556 if (!videoUrl.FromString(strBaseDir))
6560 videoUrl.AddOption("genreid", idGenre);
6561 else if (idStudio != -1)
6562 videoUrl.AddOption("studioid", idStudio);
6563 else if (idDirector != -1)
6564 videoUrl.AddOption("directorid", idDirector);
6565 else if (idYear !=-1)
6566 videoUrl.AddOption("year", idYear);
6567 else if (idArtist != -1)
6568 videoUrl.AddOption("artistid", idArtist);
6569 else if (idTag != -1)
6570 videoUrl.AddOption("tagid", idTag);
6572 videoUrl.AddOption("albumid", idAlbum);
6575 return GetMusicVideosByWhere(videoUrl.ToString(), filter, items, true, sortDescription);
6578 bool CVideoDatabase::GetRecentlyAddedMoviesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6581 filter.order = "dateAdded desc, idMovie desc";
6582 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6583 return GetMoviesByWhere(strBaseDir, filter, items);
6586 bool CVideoDatabase::GetRecentlyAddedEpisodesNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6589 filter.order = "dateAdded desc, idEpisode desc";
6590 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6591 return GetEpisodesByWhere(strBaseDir, filter, items, false);
6594 bool CVideoDatabase::GetRecentlyAddedMusicVideosNav(const CStdString& strBaseDir, CFileItemList& items, unsigned int limit)
6597 filter.order = "dateAdded desc, idMVideo desc";
6598 filter.limit = PrepareSQL("%u", limit ? limit : g_advancedSettings.m_iVideoLibraryRecentlyAddedItems);
6599 return GetMusicVideosByWhere(strBaseDir, filter, items);
6602 CStdString CVideoDatabase::GetGenreById(int id)
6604 return GetSingleValue("genre", "strGenre", PrepareSQL("idGenre=%i", id));
6607 CStdString CVideoDatabase::GetCountryById(int id)
6609 return GetSingleValue("country", "strCountry", PrepareSQL("idCountry=%i", id));
6612 CStdString CVideoDatabase::GetSetById(int id)
6614 return GetSingleValue("sets", "strSet", PrepareSQL("idSet=%i", id));
6617 CStdString CVideoDatabase::GetTagById(int id)
6619 return GetSingleValue("tag", "strTag", PrepareSQL("idTag = %i", id));
6622 CStdString CVideoDatabase::GetPersonById(int id)
6624 return GetSingleValue("actors", "strActor", PrepareSQL("idActor=%i", id));
6627 CStdString CVideoDatabase::GetStudioById(int id)
6629 return GetSingleValue("studio", "strStudio", PrepareSQL("idStudio=%i", id));
6632 CStdString CVideoDatabase::GetTvShowTitleById(int id)
6634 return GetSingleValue("tvshow", PrepareSQL("c%02d", VIDEODB_ID_TV_TITLE), PrepareSQL("idShow=%i", id));
6637 CStdString CVideoDatabase::GetMusicVideoAlbumById(int id)
6639 return GetSingleValue("musicvideo", PrepareSQL("c%02d", VIDEODB_ID_MUSICVIDEO_ALBUM), PrepareSQL("idMVideo=%i", id));
6642 bool CVideoDatabase::HasSets() const
6646 if (NULL == m_pDB.get()) return false;
6647 if (NULL == m_pDS.get()) return false;
6649 m_pDS->query("SELECT movieview.idSet,COUNT(1) AS c FROM movieview "
6650 "JOIN sets ON sets.idSet = movieview.idSet "
6651 "GROUP BY movieview.idSet HAVING c>1");
6653 bool bResult = (m_pDS->num_rows() > 0);
6659 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6664 int CVideoDatabase::GetTvShowForEpisode(int idEpisode)
6668 if (NULL == m_pDB.get()) return false;
6669 if (NULL == m_pDS2.get()) return false;
6671 // make sure we use m_pDS2, as this is called in loops using m_pDS
6672 CStdString strSQL=PrepareSQL("select idShow from episode where idEpisode=%i", idEpisode);
6673 m_pDS2->query( strSQL.c_str() );
6677 result=m_pDS2->fv(0).get_asInt();
6684 CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idEpisode);
6689 int CVideoDatabase::GetSeasonForEpisode(int idEpisode)
6692 sprintf(column, "c%0d", VIDEODB_ID_EPISODE_SEASON);
6693 CStdString id = GetSingleValue("episode", column, PrepareSQL("idEpisode=%i", idEpisode));
6696 return atoi(id.c_str());
6699 bool CVideoDatabase::HasContent()
6701 return (HasContent(VIDEODB_CONTENT_MOVIES) ||
6702 HasContent(VIDEODB_CONTENT_TVSHOWS) ||
6703 HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
6706 bool CVideoDatabase::HasContent(VIDEODB_CONTENT_TYPE type)
6708 bool result = false;
6711 if (NULL == m_pDB.get()) return false;
6712 if (NULL == m_pDS.get()) return false;
6715 if (type == VIDEODB_CONTENT_MOVIES)
6716 sql = "select count(1) from movie";
6717 else if (type == VIDEODB_CONTENT_TVSHOWS)
6718 sql = "select count(1) from tvshow";
6719 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
6720 sql = "select count(1) from musicvideo";
6721 m_pDS->query( sql.c_str() );
6724 result = (m_pDS->fv(0).get_asInt() > 0);
6730 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6735 int CVideoDatabase::GetMusicVideoCount(const CStdString& strWhere)
6739 if (NULL == m_pDB.get()) return 0;
6740 if (NULL == m_pDS.get()) return 0;
6743 strSQL.Format("select count(1) as nummovies from musicvideoview where %s",strWhere.c_str());
6744 m_pDS->query( strSQL.c_str() );
6748 iResult = m_pDS->fv("nummovies").get_asInt();
6755 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6760 ScraperPtr CVideoDatabase::GetScraperForPath( const CStdString& strPath )
6762 SScanSettings settings;
6763 return GetScraperForPath(strPath, settings);
6766 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings)
6769 return GetScraperForPath(strPath, settings, dummy);
6772 ScraperPtr CVideoDatabase::GetScraperForPath(const CStdString& strPath, SScanSettings& settings, bool& foundDirectly)
6774 foundDirectly = false;
6777 if (strPath.IsEmpty() || !m_pDB.get() || !m_pDS.get()) return ScraperPtr();
6780 CStdString strPath2;
6782 if (URIUtils::IsMultiPath(strPath))
6783 strPath2 = CMultiPathDirectory::GetFirstPath(strPath);
6787 CStdString strPath1 = URIUtils::GetDirectory(strPath2);
6788 int idPath = GetPathId(strPath1);
6792 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);
6793 m_pDS->query( strSQL.c_str() );
6797 CONTENT_TYPE content = CONTENT_NONE;
6799 { // path is stored in db
6801 if (m_pDS->fv("path.exclude").get_asBool())
6803 settings.exclude = true;
6805 return ScraperPtr();
6807 settings.exclude = false;
6809 // try and ascertain scraper for this path
6810 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6811 strcontent.ToLower();
6812 content = TranslateContent(strcontent);
6814 //FIXME paths stored should not have empty strContent
6815 //assert(content != CONTENT_NONE);
6816 CStdString scraperID = m_pDS->fv("path.strScraper").get_asString();
6819 if (!scraperID.empty() &&
6820 CAddonMgr::Get().GetAddon(scraperID, addon))
6822 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6824 return ScraperPtr();
6826 // store this path's content & settings
6827 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6828 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6829 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6830 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6834 if (content == CONTENT_NONE)
6835 { // this path is not saved in db
6836 // we must drill up until a scraper is configured
6837 CStdString strParent;
6838 while (URIUtils::GetParentPath(strPath1, strParent))
6842 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());
6843 m_pDS->query(strSQL.c_str());
6845 CONTENT_TYPE content = CONTENT_NONE;
6849 CStdString strcontent = m_pDS->fv("path.strContent").get_asString();
6850 strcontent.ToLower();
6851 if (m_pDS->fv("path.exclude").get_asBool())
6853 settings.exclude = true;
6859 content = TranslateContent(strcontent);
6862 if (content != CONTENT_NONE &&
6863 CAddonMgr::Get().GetAddon(m_pDS->fv("path.strScraper").get_asString(), addon))
6865 scraper = boost::dynamic_pointer_cast<CScraper>(addon->Clone());
6866 scraper->SetPathSettings(content, m_pDS->fv("path.strSettings").get_asString());
6867 settings.parent_name = m_pDS->fv("path.useFolderNames").get_asBool();
6868 settings.recurse = m_pDS->fv("path.scanRecursive").get_asInt();
6869 settings.noupdate = m_pDS->fv("path.noUpdate").get_asBool();
6870 settings.exclude = false;
6874 strPath1 = strParent;
6879 if (!scraper || scraper->Content() == CONTENT_NONE)
6880 return ScraperPtr();
6882 if (scraper->Content() == CONTENT_TVSHOWS)
6884 settings.recurse = 0;
6885 if(settings.parent_name) // single show
6887 settings.parent_name_root = settings.parent_name = (iFound == 1);
6891 settings.parent_name_root = settings.parent_name = (iFound == 2);
6894 else if (scraper->Content() == CONTENT_MOVIES)
6896 settings.recurse = settings.recurse - (iFound-1);
6897 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6899 else if (scraper->Content() == CONTENT_MUSICVIDEOS)
6901 settings.recurse = settings.recurse - (iFound-1);
6902 settings.parent_name_root = settings.parent_name && (!settings.recurse || iFound > 1);
6907 return ScraperPtr();
6909 foundDirectly = (iFound == 1);
6914 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
6916 return ScraperPtr();
6919 CStdString CVideoDatabase::GetContentForPath(const CStdString& strPath)
6921 SScanSettings settings;
6922 bool foundDirectly = false;
6923 ScraperPtr scraper = GetScraperForPath(strPath, settings, foundDirectly);
6926 if (scraper->Content() == CONTENT_TVSHOWS)
6927 { // check for episodes or seasons. Assumptions are:
6928 // 1. if episodes are in the path then we're in episodes.
6929 // 2. if no episodes are found, and content was set directly on this path, then we're in shows.
6930 // 3. if no episodes are found, and content was not set directly on this path, we're in seasons (assumes tvshows/seasons/episodes)
6931 CStdString sql = PrepareSQL("select count(1) from episodeview where strPath = '%s' limit 1", strPath.c_str());
6932 m_pDS->query( sql.c_str() );
6933 if (m_pDS->num_rows() && m_pDS->fv(0).get_asInt() > 0)
6935 return foundDirectly ? "tvshows" : "seasons";
6937 return TranslateContent(scraper->Content());
6942 void CVideoDatabase::GetMovieGenresByName(const CStdString& strSearch, CFileItemList& items)
6948 if (NULL == m_pDB.get()) return;
6949 if (NULL == m_pDS.get()) return;
6951 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6952 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());
6954 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmovie where genrelinkmovie.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
6955 m_pDS->query( strSQL.c_str() );
6957 while (!m_pDS->eof())
6959 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6960 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
6961 *CMediaSourceSettings::Get().GetSources("video")))
6967 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
6969 strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
6970 pItem->SetPath("videodb://movies/genres/"+ strDir);
6971 pItem->m_bIsFolder=true;
6979 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
6983 void CVideoDatabase::GetMovieCountriesByName(const CStdString& strSearch, CFileItemList& items)
6989 if (NULL == m_pDB.get()) return;
6990 if (NULL == m_pDS.get()) return;
6992 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
6993 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());
6995 strSQL=PrepareSQL("select distinct country.idCountry,country.strCountry from country,countrylinkmovie where countrylinkmovie.idCountry=country.idCountry and strCountry like '%%%s%%'", strSearch.c_str());
6996 m_pDS->query( strSQL.c_str() );
6998 while (!m_pDS->eof())
7000 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7001 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),
7002 *CMediaSourceSettings::Get().GetSources("video")))
7008 CFileItemPtr pItem(new CFileItem(m_pDS->fv("country.strCountry").get_asString()));
7010 strDir.Format("%ld/", m_pDS->fv("country.idCountry").get_asInt());
7011 pItem->SetPath("videodb://movies/genres/"+ strDir);
7012 pItem->m_bIsFolder=true;
7020 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7024 void CVideoDatabase::GetTvShowGenresByName(const CStdString& strSearch, CFileItemList& items)
7030 if (NULL == m_pDB.get()) return;
7031 if (NULL == m_pDS.get()) return;
7033 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7034 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());
7036 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinktvshow where genrelinktvshow.idGenre=genre.idGenre and strGenre like '%%%s%%'", strSearch.c_str());
7037 m_pDS->query( strSQL.c_str() );
7039 while (!m_pDS->eof())
7041 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7042 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7048 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7050 strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7051 pItem->SetPath("videodb://tvshows/genres/"+ strDir);
7052 pItem->m_bIsFolder=true;
7060 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7064 void CVideoDatabase::GetMovieActorsByName(const CStdString& strSearch, CFileItemList& items)
7070 if (NULL == m_pDB.get()) return;
7071 if (NULL == m_pDS.get()) return;
7073 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7074 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());
7076 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());
7077 m_pDS->query( strSQL.c_str() );
7079 while (!m_pDS->eof())
7081 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7082 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7088 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7090 strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7091 pItem->SetPath("videodb://movies/actors/"+ strDir);
7092 pItem->m_bIsFolder=true;
7100 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7104 void CVideoDatabase::GetTvShowsActorsByName(const CStdString& strSearch, CFileItemList& items)
7110 if (NULL == m_pDB.get()) return;
7111 if (NULL == m_pDS.get()) return;
7113 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7114 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());
7116 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());
7117 m_pDS->query( strSQL.c_str() );
7119 while (!m_pDS->eof())
7121 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7122 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7128 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7130 strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7131 pItem->SetPath("videodb://tvshows/actors/"+ strDir);
7132 pItem->m_bIsFolder=true;
7140 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7144 void CVideoDatabase::GetMusicVideoArtistsByName(const CStdString& strSearch, CFileItemList& items)
7150 if (NULL == m_pDB.get()) return;
7151 if (NULL == m_pDS.get()) return;
7154 if (!strSearch.IsEmpty())
7155 strLike = "and actors.strActor like '%%%s%%'";
7156 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7157 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());
7159 strSQL=PrepareSQL("select distinct actors.idActor,actors.strActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist "+strLike,strSearch.c_str());
7160 m_pDS->query( strSQL.c_str() );
7162 while (!m_pDS->eof())
7164 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7165 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7171 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7173 strDir.Format("%ld/", m_pDS->fv("actors.idActor").get_asInt());
7174 pItem->SetPath("videodb://musicvideos/artist/"+ strDir);
7175 pItem->m_bIsFolder=true;
7183 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7187 void CVideoDatabase::GetMusicVideoGenresByName(const CStdString& strSearch, CFileItemList& items)
7193 if (NULL == m_pDB.get()) return;
7194 if (NULL == m_pDS.get()) return;
7196 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7197 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());
7199 strSQL=PrepareSQL("select distinct genre.idGenre,genre.strGenre from genre,genrelinkmusicvideo where genrelinkmusicvideo.idGenre=genre.idGenre and genre.strGenre like '%%%s%%'", strSearch.c_str());
7200 m_pDS->query( strSQL.c_str() );
7202 while (!m_pDS->eof())
7204 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7205 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7211 CFileItemPtr pItem(new CFileItem(m_pDS->fv("genre.strGenre").get_asString()));
7213 strDir.Format("%ld/", m_pDS->fv("genre.idGenre").get_asInt());
7214 pItem->SetPath("videodb://musicvideos/genres/"+ strDir);
7215 pItem->m_bIsFolder=true;
7223 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7227 void CVideoDatabase::GetMusicVideoAlbumsByName(const CStdString& strSearch, CFileItemList& items)
7233 if (NULL == m_pDB.get()) return;
7234 if (NULL == m_pDS.get()) return;
7237 if (!strSearch.IsEmpty())
7239 strLike.Format("and musicvideo.c%02d",VIDEODB_ID_MUSICVIDEO_ALBUM);
7240 strLike += "like '%%s%%%'";
7242 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7243 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());
7246 if (!strLike.IsEmpty())
7247 strLike = "where "+strLike.Mid(4);
7248 strSQL=PrepareSQL("select distinct musicvideo.c%02d,musicvideo.idMVideo from musicvideo"+strLike,VIDEODB_ID_MUSICVIDEO_ALBUM,strSearch.c_str());
7250 m_pDS->query( strSQL.c_str() );
7252 while (!m_pDS->eof())
7254 if (m_pDS->fv(0).get_asString().empty())
7260 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7261 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7267 CFileItemPtr pItem(new CFileItem(m_pDS->fv(0).get_asString()));
7269 strDir.Format("%ld", m_pDS->fv(1).get_asInt());
7270 pItem->SetPath("videodb://musicvideos/titles/"+ strDir);
7271 pItem->m_bIsFolder=false;
7279 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7283 void CVideoDatabase::GetMusicVideosByAlbum(const CStdString& strSearch, CFileItemList& items)
7289 if (NULL == m_pDB.get()) return;
7290 if (NULL == m_pDS.get()) return;
7292 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7293 strSQL = PrepareSQL("select 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());
7295 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());
7296 m_pDS->query( strSQL.c_str() );
7298 while (!m_pDS->eof())
7300 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7301 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7307 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" - "+m_pDS->fv(2).get_asString()));
7309 strDir.Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7311 pItem->SetPath("videodb://"+ strDir);
7312 pItem->m_bIsFolder=false;
7320 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7324 bool CVideoDatabase::GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList &items, bool checkLocks /*= true*/, const SortDescription &sortDescription /* = SortDescription() */)
7331 if (NULL == m_pDB.get()) return false;
7332 if (NULL == m_pDS.get()) return false;
7336 CStdString strSQL = "select %s from musicvideoview ";
7337 CVideoDbUrl videoUrl;
7338 CStdString strSQLExtra;
7339 Filter extFilter = filter;
7340 SortDescription sorting = sortDescription;
7341 if (!BuildSQL(baseDir, strSQLExtra, extFilter, strSQLExtra, videoUrl, sorting))
7344 // Apply the limiting directly here if there's no special sorting but limiting
7345 if (extFilter.limit.empty() &&
7346 sorting.sortBy == SortByNone &&
7347 (sorting.limitStart > 0 || sorting.limitEnd > 0))
7349 total = (int)strtol(GetSingleValue(PrepareSQL(strSQL, "COUNT(1)") + strSQLExtra, m_pDS).c_str(), NULL, 10);
7350 strSQLExtra += DatabaseUtils::BuildLimitClause(sorting.limitEnd, sorting.limitStart);
7353 strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
7355 int iRowsFound = RunQuery(strSQL);
7356 if (iRowsFound <= 0)
7357 return iRowsFound == 0;
7359 // store the total value of items as a property
7360 if (total < iRowsFound)
7362 items.SetProperty("total", total);
7364 DatabaseResults results;
7365 results.reserve(iRowsFound);
7366 if (!SortUtils::SortFromDataset(sorting, MediaTypeMusicVideo, m_pDS, results))
7369 // get data from returned rows
7370 items.Reserve(results.size());
7371 // get songs from returned subtable
7372 const query_data &data = m_pDS->get_result_set().records;
7373 for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
7375 unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
7376 const dbiplus::sql_record* const record = data.at(targetRow);
7378 CVideoInfoTag musicvideo = GetDetailsForMusicVideo(record);
7379 if (!checkLocks || CProfilesManager::Get().GetMasterProfile().getLockMode() == LOCK_MODE_EVERYONE || g_passwordManager.bMasterUser ||
7380 g_passwordManager.IsDatabasePathUnlocked(musicvideo.m_strPath, *CMediaSourceSettings::Get().GetSources("video")))
7382 CFileItemPtr item(new CFileItem(musicvideo));
7384 CVideoDbUrl itemUrl = videoUrl;
7385 CStdString path; path.Format("%ld", record->at(0).get_asInt());
7386 itemUrl.AppendPath(path);
7387 item->SetPath(itemUrl.ToString());
7389 item->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, musicvideo.m_playCount > 0);
7400 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7405 unsigned int CVideoDatabase::GetMusicVideoIDs(const CStdString& strWhere, vector<pair<int,int> > &songIDs)
7409 if (NULL == m_pDB.get()) return 0;
7410 if (NULL == m_pDS.get()) return 0;
7412 CStdString strSQL = "select distinct idMVideo from musicvideoview " + strWhere;
7413 if (!m_pDS->query(strSQL.c_str())) return 0;
7415 if (m_pDS->num_rows() == 0)
7420 songIDs.reserve(m_pDS->num_rows());
7421 while (!m_pDS->eof())
7423 songIDs.push_back(make_pair<int,int>(2,m_pDS->fv(0).get_asInt()));
7427 return songIDs.size();
7431 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7436 bool CVideoDatabase::GetRandomMusicVideo(CFileItem* item, int& idSong, const CStdString& strWhere)
7442 if (NULL == m_pDB.get()) return false;
7443 if (NULL == m_pDS.get()) return false;
7445 // We don't use PrepareSQL here, as the WHERE clause is already formatted.
7447 strSQL.Format("select * from musicvideoview where %s",strWhere.c_str());
7448 strSQL += PrepareSQL(" order by RANDOM() limit 1");
7449 CLog::Log(LOGDEBUG, "%s query = %s", __FUNCTION__, strSQL.c_str());
7451 if (!m_pDS->query(strSQL.c_str()))
7453 int iRowsFound = m_pDS->num_rows();
7454 if (iRowsFound != 1)
7459 *item->GetVideoInfoTag() = GetDetailsForMusicVideo(m_pDS);
7460 CStdString path; path.Format("videodb://musicvideos/titles/%ld",item->GetVideoInfoTag()->m_iDbId);
7461 item->SetPath(path);
7462 idSong = m_pDS->fv("idMVideo").get_asInt();
7463 item->SetLabel(item->GetVideoInfoTag()->m_strTitle);
7469 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strWhere.c_str());
7474 int CVideoDatabase::GetMatchingMusicVideo(const CStdString& strArtist, const CStdString& strAlbum, const CStdString& strTitle)
7478 if (NULL == m_pDB.get()) return -1;
7479 if (NULL == m_pDS.get()) return -1;
7482 if (strAlbum.IsEmpty() && strTitle.IsEmpty())
7483 { // we want to return matching artists only
7484 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7485 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());
7487 strSQL=PrepareSQL("select distinct actors.idActor from artistlinkmusicvideo,actors where actors.idActor=artistlinkmusicvideo.idArtist and actors.strActor like '%s'",strArtist.c_str());
7490 { // we want to return the matching musicvideo
7491 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7492 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());
7494 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());
7496 m_pDS->query( strSQL.c_str() );
7501 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7502 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7508 int lResult = m_pDS->fv(0).get_asInt();
7514 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
7519 void CVideoDatabase::GetMoviesByName(const CStdString& strSearch, CFileItemList& items)
7525 if (NULL == m_pDB.get()) return;
7526 if (NULL == m_pDS.get()) return;
7528 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7529 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());
7531 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());
7532 m_pDS->query( strSQL.c_str() );
7534 while (!m_pDS->eof())
7536 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7537 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7543 int movieId = m_pDS->fv("movie.idMovie").get_asInt();
7544 int setId = m_pDS->fv("movie.idSet").get_asInt();
7545 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7547 if (setId <= 0 || !CSettings::Get().GetBool("videolibrary.groupmoviesets"))
7548 path.Format("videodb://movies/titles/%i", movieId);
7550 path.Format("videodb://movies/sets/%i/%i", setId, movieId);
7551 pItem->SetPath(path);
7552 pItem->m_bIsFolder=false;
7560 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7564 void CVideoDatabase::GetTvShowsByName(const CStdString& strSearch, CFileItemList& items)
7570 if (NULL == m_pDB.get()) return;
7571 if (NULL == m_pDS.get()) return;
7573 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7574 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());
7576 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());
7577 m_pDS->query( strSQL.c_str() );
7579 while (!m_pDS->eof())
7581 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7582 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7588 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7590 strDir.Format("tvshows/titles/%ld/", m_pDS->fv("tvshow.idShow").get_asInt());
7592 pItem->SetPath("videodb://"+ strDir);
7593 pItem->m_bIsFolder=true;
7594 pItem->GetVideoInfoTag()->m_iDbId = m_pDS->fv("tvshow.idShow").get_asInt();
7602 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7606 void CVideoDatabase::GetEpisodesByName(const CStdString& strSearch, CFileItemList& items)
7612 if (NULL == m_pDB.get()) return;
7613 if (NULL == m_pDS.get()) return;
7615 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7616 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());
7618 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());
7619 m_pDS->query( strSQL.c_str() );
7621 while (!m_pDS->eof())
7623 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7624 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7630 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7631 CStdString path; path.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());
7632 pItem->SetPath(path);
7633 pItem->m_bIsFolder=false;
7641 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7645 void CVideoDatabase::GetMusicVideosByName(const CStdString& strSearch, CFileItemList& items)
7647 // Alternative searching - not quite as fast though due to
7648 // retrieving all information
7649 // 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()));
7650 // GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
7655 if (NULL == m_pDB.get()) return;
7656 if (NULL == m_pDS.get()) return;
7658 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7659 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());
7661 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());
7662 m_pDS->query( strSQL.c_str() );
7664 while (!m_pDS->eof())
7666 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7667 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7673 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7675 strDir.Format("3/2/%ld",m_pDS->fv("musicvideo.idMVideo").get_asInt());
7677 pItem->SetPath("videodb://"+ strDir);
7678 pItem->m_bIsFolder=false;
7686 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7690 void CVideoDatabase::GetEpisodesByPlot(const CStdString& strSearch, CFileItemList& items)
7692 // Alternative searching - not quite as fast though due to
7693 // retrieving all information
7695 // 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());
7696 // 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());
7697 // GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
7703 if (NULL == m_pDB.get()) return;
7704 if (NULL == m_pDS.get()) return;
7706 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7707 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());
7709 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());
7710 m_pDS->query( strSQL.c_str() );
7712 while (!m_pDS->eof())
7714 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7715 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7721 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()+" ("+m_pDS->fv(4).get_asString()+")"));
7722 CStdString path; path.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());
7723 pItem->SetPath(path);
7724 pItem->m_bIsFolder=false;
7732 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7736 void CVideoDatabase::GetMoviesByPlot(const CStdString& strSearch, CFileItemList& items)
7742 if (NULL == m_pDB.get()) return;
7743 if (NULL == m_pDS.get()) return;
7745 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7746 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());
7748 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());
7750 m_pDS->query( strSQL.c_str() );
7752 while (!m_pDS->eof())
7754 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7755 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv(2).get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7761 CFileItemPtr pItem(new CFileItem(m_pDS->fv(1).get_asString()));
7762 CStdString path; path.Format("videodb://movies/titles/%ld", m_pDS->fv(0).get_asInt());
7763 pItem->SetPath(path);
7764 pItem->m_bIsFolder=false;
7774 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7778 void CVideoDatabase::GetMovieDirectorsByName(const CStdString& strSearch, CFileItemList& 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 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());
7790 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());
7792 m_pDS->query( strSQL.c_str() );
7794 while (!m_pDS->eof())
7796 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7797 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7804 strDir.Format("%ld/", m_pDS->fv("directorlinkmovie.idDirector").get_asInt());
7805 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7807 pItem->SetPath("videodb://movies/directors/"+ strDir);
7808 pItem->m_bIsFolder=true;
7816 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7820 void CVideoDatabase::GetTvShowsDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7826 if (NULL == m_pDB.get()) return;
7827 if (NULL == m_pDS.get()) return;
7829 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7830 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());
7832 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());
7834 m_pDS->query( strSQL.c_str() );
7836 while (!m_pDS->eof())
7838 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7839 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7846 strDir.Format("%ld/", m_pDS->fv("directorlinktvshow.idDirector").get_asInt());
7847 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7849 pItem->SetPath("videodb://tvshows/studios/"+ strDir);
7850 pItem->m_bIsFolder=true;
7858 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7862 void CVideoDatabase::GetMusicVideoDirectorsByName(const CStdString& strSearch, CFileItemList& items)
7868 if (NULL == m_pDB.get()) return;
7869 if (NULL == m_pDS.get()) return;
7871 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7872 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());
7874 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());
7876 m_pDS->query( strSQL.c_str() );
7878 while (!m_pDS->eof())
7880 if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser)
7881 if (!g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("path.strPath").get_asString()),*CMediaSourceSettings::Get().GetSources("video")))
7888 strDir.Format("%ld/", m_pDS->fv("directorlinkmusicvideo.idDirector").get_asInt());
7889 CFileItemPtr pItem(new CFileItem(m_pDS->fv("actors.strActor").get_asString()));
7891 pItem->SetPath("videodb://musicvideos/albums/"+ strDir);
7892 pItem->m_bIsFolder=true;
7900 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
7904 void CVideoDatabase::CleanDatabase(CGUIDialogProgressBarHandle* handle, const set<int>* paths, bool showProgress)
7906 CGUIDialogProgress *progress=NULL;
7909 if (NULL == m_pDB.get()) return;
7910 if (NULL == m_pDS.get()) return;
7912 unsigned int time = XbmcThreads::SystemClockMillis();
7913 CLog::Log(LOGNOTICE, "%s: Starting videodatabase cleanup ..", __FUNCTION__);
7914 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanStarted");
7918 // find all the files
7922 if (paths->size() == 0)
7924 RollbackTransaction();
7925 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
7929 CStdString strPaths;
7930 for (std::set<int>::const_iterator i = paths->begin(); i != paths->end(); ++i)
7931 strPaths.AppendFormat(",%i",*i);
7932 sql = PrepareSQL("select * from files,path where files.idPath=path.idPath and path.idPath in (%s)",strPaths.Mid(1).c_str());
7935 sql = "select * from files, path where files.idPath = path.idPath";
7937 m_pDS->query(sql.c_str());
7938 if (m_pDS->num_rows() == 0) return;
7942 handle->SetTitle(g_localizeStrings.Get(700));
7943 handle->SetText("");
7945 else if (showProgress)
7947 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
7950 progress->SetHeading(700);
7951 progress->SetLine(0, "");
7952 progress->SetLine(1, 313);
7953 progress->SetLine(2, 330);
7954 progress->SetPercentage(0);
7955 progress->StartModal();
7956 progress->ShowProgressBar(true);
7960 CStdString filesToDelete = "";
7961 CStdString moviesToDelete = "";
7962 CStdString episodesToDelete = "";
7963 CStdString musicVideosToDelete = "";
7965 std::vector<int> movieIDs;
7966 std::vector<int> episodeIDs;
7967 std::vector<int> musicVideoIDs;
7969 int total = m_pDS->num_rows();
7973 VECSOURCES *pShares = CMediaSourceSettings::Get().GetSources("video");
7975 while (!m_pDS->eof())
7977 CStdString path = m_pDS->fv("path.strPath").get_asString();
7978 CStdString fileName = m_pDS->fv("files.strFileName").get_asString();
7979 CStdString fullPath;
7980 ConstructPath(fullPath,path,fileName);
7982 // get the first stacked file
7983 if (URIUtils::IsStack(fullPath))
7984 fullPath = CStackDirectory::GetFirstStackedFile(fullPath);
7986 // check if we have a internet related file that is part of a media source
7987 if (URIUtils::IsInternetStream(fullPath, true) && CUtil::GetMatchingSource(fullPath, *pShares, bIsSource) > -1)
7989 if (!CFile::Exists(fullPath, false))
7990 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
7994 // remove optical, internet related and non-existing files
7995 // note: this will also remove entries from previously existing media sources
7996 if (URIUtils::IsOnDVD(fullPath) || URIUtils::IsInternetStream(fullPath, true) || !CFile::Exists(fullPath, false))
7997 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
8004 progress->SetPercentage(current * 100 / total);
8005 progress->Progress();
8006 if (progress->IsCanceled())
8010 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8016 handle->SetPercentage(current/(float)total*100);
8023 // Add any files that don't have a valid idPath entry to the filesToDelete list.
8024 sql = "select files.idFile from files where idPath not in (select idPath from path)";
8025 m_pDS->query(sql.c_str());
8026 while (!m_pDS->eof())
8028 filesToDelete += m_pDS->fv("files.idFile").get_asString() + ",";
8033 if ( ! filesToDelete.IsEmpty() )
8035 filesToDelete.TrimRight(",");
8036 // now grab them movies
8037 sql = PrepareSQL("select idMovie from movie where idFile in (%s)",filesToDelete.c_str());
8038 m_pDS->query(sql.c_str());
8039 while (!m_pDS->eof())
8041 movieIDs.push_back(m_pDS->fv(0).get_asInt());
8042 moviesToDelete += m_pDS->fv(0).get_asString() + ",";
8046 // now grab them episodes
8047 sql = PrepareSQL("select idEpisode from episode where idFile in (%s)",filesToDelete.c_str());
8048 m_pDS->query(sql.c_str());
8049 while (!m_pDS->eof())
8051 episodeIDs.push_back(m_pDS->fv(0).get_asInt());
8052 episodesToDelete += m_pDS->fv(0).get_asString() + ",";
8057 // now grab them musicvideos
8058 sql = PrepareSQL("select idMVideo from musicvideo where idFile in (%s)",filesToDelete.c_str());
8059 m_pDS->query(sql.c_str());
8060 while (!m_pDS->eof())
8062 musicVideoIDs.push_back(m_pDS->fv(0).get_asInt());
8063 musicVideosToDelete += m_pDS->fv(0).get_asString() + ",";
8071 progress->SetPercentage(100);
8072 progress->Progress();
8075 if ( ! filesToDelete.IsEmpty() )
8077 filesToDelete = "(" + filesToDelete + ")";
8078 CLog::Log(LOGDEBUG, "%s: Cleaning files table", __FUNCTION__);
8079 sql = "delete from files where idFile in " + filesToDelete;
8080 m_pDS->exec(sql.c_str());
8082 CLog::Log(LOGDEBUG, "%s: Cleaning streamdetails table", __FUNCTION__);
8083 sql = "delete from streamdetails where idFile in " + filesToDelete;
8084 m_pDS->exec(sql.c_str());
8086 CLog::Log(LOGDEBUG, "%s: Cleaning bookmark table", __FUNCTION__);
8087 sql = "delete from bookmark where idFile in " + filesToDelete;
8088 m_pDS->exec(sql.c_str());
8090 CLog::Log(LOGDEBUG, "%s: Cleaning settings table", __FUNCTION__);
8091 sql = "delete from settings where idFile in " + filesToDelete;
8092 m_pDS->exec(sql.c_str());
8094 CLog::Log(LOGDEBUG, "%s: Cleaning stacktimes table", __FUNCTION__);
8095 sql = "delete from stacktimes where idFile in " + filesToDelete;
8096 m_pDS->exec(sql.c_str());
8099 if ( ! moviesToDelete.IsEmpty() )
8101 moviesToDelete = "(" + moviesToDelete.TrimRight(",") + ")";
8103 CLog::Log(LOGDEBUG, "%s: Cleaning movie table", __FUNCTION__);
8104 sql = "delete from movie where idMovie in " + moviesToDelete;
8105 m_pDS->exec(sql.c_str());
8107 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkmovie table", __FUNCTION__);
8108 sql = "delete from actorlinkmovie where idMovie in " + moviesToDelete;
8109 m_pDS->exec(sql.c_str());
8111 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmovie table", __FUNCTION__);
8112 sql = "delete from directorlinkmovie where idMovie in " + moviesToDelete;
8113 m_pDS->exec(sql.c_str());
8115 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkmovie table", __FUNCTION__);
8116 sql = "delete from writerlinkmovie where idMovie in " + moviesToDelete;
8117 m_pDS->exec(sql.c_str());
8119 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmovie table", __FUNCTION__);
8120 sql = "delete from genrelinkmovie where idMovie in " + moviesToDelete;
8121 m_pDS->exec(sql.c_str());
8123 CLog::Log(LOGDEBUG, "%s: Cleaning countrylinkmovie table", __FUNCTION__);
8124 sql = "delete from countrylinkmovie where idMovie in " + moviesToDelete;
8125 m_pDS->exec(sql.c_str());
8127 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmovie table", __FUNCTION__);
8128 sql = "delete from studiolinkmovie where idMovie in " + moviesToDelete;
8129 m_pDS->exec(sql.c_str());
8132 if ( ! episodesToDelete.IsEmpty() )
8134 episodesToDelete = "(" + episodesToDelete.TrimRight(",") + ")";
8136 CLog::Log(LOGDEBUG, "%s: Cleaning episode table", __FUNCTION__);
8137 sql = "delete from episode where idEpisode in " + episodesToDelete;
8138 m_pDS->exec(sql.c_str());
8140 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinkepisode table", __FUNCTION__);
8141 sql = "delete from actorlinkepisode where idEpisode in " + episodesToDelete;
8142 m_pDS->exec(sql.c_str());
8144 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkepisode table", __FUNCTION__);
8145 sql = "delete from directorlinkepisode where idEpisode in " + episodesToDelete;
8146 m_pDS->exec(sql.c_str());
8148 CLog::Log(LOGDEBUG, "%s: Cleaning writerlinkepisode table", __FUNCTION__);
8149 sql = "delete from writerlinkepisode where idEpisode in " + episodesToDelete;
8150 m_pDS->exec(sql.c_str());
8153 CLog::Log(LOGDEBUG, "%s: Cleaning paths that don't exist and have content set...", __FUNCTION__);
8154 sql = "select * from path where not (strContent='' and strSettings='' and strHash='' and exclude!=1)";
8155 m_pDS->query(sql.c_str());
8157 while (!m_pDS->eof())
8159 if (!CDirectory::Exists(m_pDS->fv("path.strPath").get_asString()))
8160 strIds.AppendFormat("%i,", m_pDS->fv("path.idPath").get_asInt());
8164 if (!strIds.IsEmpty())
8166 strIds.TrimRight(",");
8167 sql = PrepareSQL("delete from path where idPath in (%s)",strIds.c_str());
8168 m_pDS->exec(sql.c_str());
8169 sql = PrepareSQL("delete from tvshowlinkpath where idPath in (%s)",strIds.c_str());
8170 m_pDS->exec(sql.c_str());
8172 sql = "delete from tvshowlinkpath where idPath not in (select idPath from path)";
8173 m_pDS->exec(sql.c_str());
8175 CLog::Log(LOGDEBUG, "%s: Cleaning tvshow table", __FUNCTION__);
8176 sql = "delete from tvshow where idShow not in (select idShow from tvshowlinkpath)";
8177 m_pDS->exec(sql.c_str());
8179 std::vector<int> tvshowIDs;
8180 CStdString showsToDelete;
8181 sql = "select tvshow.idShow from tvshow "
8182 "join tvshowlinkpath on tvshow.idShow=tvshowlinkpath.idShow "
8183 "join path on path.idPath=tvshowlinkpath.idPath "
8184 "where tvshow.idShow not in (select idShow from episode) "
8185 "and path.strContent=''";
8186 m_pDS->query(sql.c_str());
8187 while (!m_pDS->eof())
8189 tvshowIDs.push_back(m_pDS->fv(0).get_asInt());
8190 showsToDelete += m_pDS->fv(0).get_asString() + ",";
8194 if (!showsToDelete.IsEmpty())
8196 sql = "delete from tvshow where idShow in (" + showsToDelete.TrimRight(",") + ")";
8197 m_pDS->exec(sql.c_str());
8200 CLog::Log(LOGDEBUG, "%s: Cleaning actorlinktvshow table", __FUNCTION__);
8201 sql = "delete from actorlinktvshow where idShow not in (select idShow from tvshow)";
8202 m_pDS->exec(sql.c_str());
8204 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinktvshow table", __FUNCTION__);
8205 sql = "delete from directorlinktvshow where idShow not in (select idShow from tvshow)";
8206 m_pDS->exec(sql.c_str());
8208 CLog::Log(LOGDEBUG, "%s: Cleaning tvshowlinkpath table", __FUNCTION__);
8209 sql = "delete from tvshowlinkpath where idShow not in (select idShow from tvshow)";
8210 m_pDS->exec(sql.c_str());
8212 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinktvshow table", __FUNCTION__);
8213 sql = "delete from genrelinktvshow where idShow not in (select idShow from tvshow)";
8214 m_pDS->exec(sql.c_str());
8216 CLog::Log(LOGDEBUG, "%s: Cleaning seasons table", __FUNCTION__);
8217 sql = "delete from seasons where idShow not in (select idShow from tvshow)";
8218 m_pDS->exec(sql.c_str());
8220 CLog::Log(LOGDEBUG, "%s: Cleaning movielinktvshow table", __FUNCTION__);
8221 sql = "delete from movielinktvshow where idShow not in (select idShow from tvshow)";
8222 m_pDS->exec(sql.c_str());
8223 sql = "delete from movielinktvshow where idMovie not in (select distinct idMovie from movie)";
8224 m_pDS->exec(sql.c_str());
8226 if ( ! musicVideosToDelete.IsEmpty() )
8228 musicVideosToDelete = "(" + musicVideosToDelete.TrimRight(",") + ")";
8230 CLog::Log(LOGDEBUG, "%s: Cleaning musicvideo table", __FUNCTION__);
8231 sql = "delete from musicvideo where idMVideo in " + musicVideosToDelete;
8232 m_pDS->exec(sql.c_str());
8234 CLog::Log(LOGDEBUG, "%s: Cleaning artistlinkmusicvideo table", __FUNCTION__);
8235 sql = "delete from artistlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8236 m_pDS->exec(sql.c_str());
8238 CLog::Log(LOGDEBUG, "%s: Cleaning directorlinkmusicvideo table" ,__FUNCTION__);
8239 sql = "delete from directorlinkmusicvideo where idMVideo in " + musicVideosToDelete;
8240 m_pDS->exec(sql.c_str());
8242 CLog::Log(LOGDEBUG, "%s: Cleaning genrelinkmusicvideo table" ,__FUNCTION__);
8243 sql = "delete from genrelinkmusicvideo where idMVideo in " + musicVideosToDelete;
8244 m_pDS->exec(sql.c_str());
8246 CLog::Log(LOGDEBUG, "%s: Cleaning studiolinkmusicvideo table", __FUNCTION__);
8247 sql = "delete from studiolinkmusicvideo where idMVideo in " + musicVideosToDelete;
8248 m_pDS->exec(sql.c_str());
8251 CLog::Log(LOGDEBUG, "%s: Cleaning path table", __FUNCTION__);
8252 sql.Format("delete from path where strContent='' and strSettings='' and strHash='' and exclude!=1 "
8253 "and idPath not in (select distinct idPath from files) "
8254 "and idPath not in (select distinct idPath from tvshowlinkpath) "
8255 "and idPath not in (select distinct c%02d from movie) "
8256 "and idPath not in (select distinct c%02d from tvshow) "
8257 "and idPath not in (select distinct c%02d from episode) "
8258 "and idPath not in (select distinct c%02d from musicvideo)"
8259 , VIDEODB_ID_PARENTPATHID, VIDEODB_ID_TV_PARENTPATHID, VIDEODB_ID_EPISODE_PARENTPATHID, VIDEODB_ID_MUSICVIDEO_PARENTPATHID );
8260 m_pDS->exec(sql.c_str());
8262 CLog::Log(LOGDEBUG, "%s: Cleaning genre table", __FUNCTION__);
8263 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)";
8264 m_pDS->exec(sql.c_str());
8266 CLog::Log(LOGDEBUG, "%s: Cleaning country table", __FUNCTION__);
8267 sql = "delete from country where idCountry not in (select distinct idCountry from countrylinkmovie)";
8268 m_pDS->exec(sql.c_str());
8270 CLog::Log(LOGDEBUG, "%s: Cleaning actor table of actors, directors and writers", __FUNCTION__);
8271 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)";
8272 m_pDS->exec(sql.c_str());
8274 CLog::Log(LOGDEBUG, "%s: Cleaning studio table", __FUNCTION__);
8275 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)";
8276 m_pDS->exec(sql.c_str());
8278 CLog::Log(LOGDEBUG, "%s: Cleaning set table", __FUNCTION__);
8279 sql = "delete from sets where idSet not in (select distinct idSet from movie)";
8280 m_pDS->exec(sql.c_str());
8282 CommitTransaction();
8285 handle->SetTitle(g_localizeStrings.Get(331));
8289 CUtil::DeleteVideoDatabaseDirectoryCache();
8291 time = XbmcThreads::SystemClockMillis() - time;
8292 CLog::Log(LOGNOTICE, "%s: Cleaning videodatabase done. Operation took %s", __FUNCTION__, StringUtils::SecondsToTimeString(time / 1000).c_str());
8294 for (unsigned int i = 0; i < movieIDs.size(); i++)
8295 AnnounceRemove("movie", movieIDs[i]);
8297 for (unsigned int i = 0; i < episodeIDs.size(); i++)
8298 AnnounceRemove("episode", episodeIDs[i]);
8300 for (unsigned int i = 0; i < tvshowIDs.size(); i++)
8301 AnnounceRemove("tvshow", tvshowIDs[i]);
8303 for (unsigned int i = 0; i < musicVideoIDs.size(); i++)
8304 AnnounceRemove("musicvideo", musicVideoIDs[i]);
8308 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8313 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnCleanFinished");
8316 void CVideoDatabase::DumpToDummyFiles(const CStdString &path)
8319 CFileItemList items;
8320 GetTvShowsByWhere("videodb://tvshows/titles/", "", items);
8321 CStdString showPath = URIUtils::AddFileToFolder(path, "shows");
8322 CDirectory::Create(showPath);
8323 for (int i = 0; i < items.Size(); i++)
8325 // create a folder in this directory
8326 CStdString showName = CUtil::MakeLegalFileName(items[i]->GetVideoInfoTag()->m_strShowTitle);
8327 CStdString TVFolder = URIUtils::AddFileToFolder(showPath, showName);
8328 if (CDirectory::Create(TVFolder))
8329 { // right - grab the episodes and dump them as well
8330 CFileItemList episodes;
8331 Filter filter(PrepareSQL("idShow=%i", items[i]->GetVideoInfoTag()->m_iDbId));
8332 GetEpisodesByWhere("videodb://tvshows/titles/", filter, episodes);
8333 for (int i = 0; i < episodes.Size(); i++)
8335 CVideoInfoTag *tag = episodes[i]->GetVideoInfoTag();
8337 episode.Format("%s.s%02de%02d.avi", showName.c_str(), tag->m_iSeason, tag->m_iEpisode);
8339 CStdString episodePath = URIUtils::AddFileToFolder(TVFolder, episode);
8341 if (file.OpenForWrite(episodePath))
8348 GetMoviesByWhere("videodb://movies/titles/", "", items);
8349 CStdString moviePath = URIUtils::AddFileToFolder(path, "movies");
8350 CDirectory::Create(moviePath);
8351 for (int i = 0; i < items.Size(); i++)
8353 CVideoInfoTag *tag = items[i]->GetVideoInfoTag();
8355 movie.Format("%s.avi", tag->m_strTitle.c_str());
8357 if (file.OpenForWrite(URIUtils::AddFileToFolder(moviePath, movie)))
8362 void CVideoDatabase::ExportToXML(const CStdString &path, bool singleFiles /* = false */, bool images /* = false */, bool actorThumbs /* false */, bool overwrite /*=false*/)
8364 CGUIDialogProgress *progress=NULL;
8367 if (NULL == m_pDB.get()) return;
8368 if (NULL == m_pDS.get()) return;
8369 if (NULL == m_pDS2.get()) return;
8371 // create a 3rd dataset as well as GetEpisodeDetails() etc. uses m_pDS2, and we need to do 3 nested queries on tv shows
8372 auto_ptr<Dataset> pDS;
8373 pDS.reset(m_pDB->CreateDataset());
8374 if (NULL == pDS.get()) return;
8376 auto_ptr<Dataset> pDS2;
8377 pDS2.reset(m_pDB->CreateDataset());
8378 if (NULL == pDS2.get()) return;
8380 // if we're exporting to a single folder, we export thumbs as well
8381 CStdString exportRoot = URIUtils::AddFileToFolder(path, "xbmc_videodb_" + CDateTime::GetCurrentDateTime().GetAsDBDate());
8382 CStdString xmlFile = URIUtils::AddFileToFolder(exportRoot, "videodb.xml");
8383 CStdString actorsDir = URIUtils::AddFileToFolder(exportRoot, "actors");
8384 CStdString moviesDir = URIUtils::AddFileToFolder(exportRoot, "movies");
8385 CStdString musicvideosDir = URIUtils::AddFileToFolder(exportRoot, "musicvideos");
8386 CStdString tvshowsDir = URIUtils::AddFileToFolder(exportRoot, "tvshows");
8392 CDirectory::Remove(exportRoot);
8393 CDirectory::Create(exportRoot);
8394 CDirectory::Create(actorsDir);
8395 CDirectory::Create(moviesDir);
8396 CDirectory::Create(musicvideosDir);
8397 CDirectory::Create(tvshowsDir);
8400 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8402 CStdString sql = "select * from movieview";
8404 m_pDS->query(sql.c_str());
8408 progress->SetHeading(647);
8409 progress->SetLine(0, 650);
8410 progress->SetLine(1, "");
8411 progress->SetLine(2, "");
8412 progress->SetPercentage(0);
8413 progress->StartModal();
8414 progress->ShowProgressBar(true);
8417 int total = m_pDS->num_rows();
8420 // create our xml document
8421 CXBMCTinyXML xmlDoc;
8422 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8423 xmlDoc.InsertEndChild(decl);
8424 TiXmlNode *pMain = NULL;
8429 TiXmlElement xmlMainElement("videodb");
8430 pMain = xmlDoc.InsertEndChild(xmlMainElement);
8431 XMLUtils::SetInt(pMain,"version", GetExportVersion());
8434 while (!m_pDS->eof())
8436 CVideoInfoTag movie = GetDetailsForMovie(m_pDS, true);
8437 // strip paths to make them relative
8438 if (movie.m_strTrailer.Mid(0,movie.m_strPath.size()).Equals(movie.m_strPath))
8439 movie.m_strTrailer = movie.m_strTrailer.Mid(movie.m_strPath.size());
8440 map<string, string> artwork;
8441 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8443 TiXmlElement additionalNode("art");
8444 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8445 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8446 movie.Save(pMain, "movie", true, &additionalNode);
8449 movie.Save(pMain, "movie", !singleFiles);
8451 // reset old skip state
8456 progress->SetLine(1, movie.m_strTitle);
8457 progress->SetPercentage(current * 100 / total);
8458 progress->Progress();
8459 if (progress->IsCanceled())
8467 CFileItem item(movie.m_strFileNameAndPath,false);
8468 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8470 if (!item.Exists(false))
8472 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8477 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8479 if (item.IsOpticalMediaFile())
8481 nfoFile = URIUtils::AddFileToFolder(
8482 URIUtils::GetParentPath(nfoFile),
8483 URIUtils::GetFileName(nfoFile));
8486 if (overwrite || !CFile::Exists(nfoFile, false))
8488 if(!xmlDoc.SaveFile(nfoFile))
8490 CLog::Log(LOGERROR, "%s: Movie nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8491 bSkip = ExportSkipEntry(nfoFile);
8508 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8509 xmlDoc.InsertEndChild(decl);
8512 if (images && !bSkip)
8516 CStdString strFileName(movie.m_strTitle);
8517 if (movie.m_iYear > 0)
8518 strFileName.AppendFormat("_%i", movie.m_iYear);
8519 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8521 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8523 CStdString savedThumb = item.GetLocalArt(i->first, false);
8524 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8527 ExportActorThumbs(actorsDir, movie, singleFiles, overwrite);
8534 // find all musicvideos
8535 sql = "select * from musicvideoview";
8537 m_pDS->query(sql.c_str());
8539 total = m_pDS->num_rows();
8542 while (!m_pDS->eof())
8544 CVideoInfoTag movie = GetDetailsForMusicVideo(m_pDS, true);
8545 map<string, string> artwork;
8546 if (GetArtForItem(movie.m_iDbId, movie.m_type, artwork) && !singleFiles)
8548 TiXmlElement additionalNode("art");
8549 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8550 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8551 movie.Save(pMain, "musicvideo", true, &additionalNode);
8554 movie.Save(pMain, "musicvideo", !singleFiles);
8556 // reset old skip state
8561 progress->SetLine(1, movie.m_strTitle);
8562 progress->SetPercentage(current * 100 / total);
8563 progress->Progress();
8564 if (progress->IsCanceled())
8572 CFileItem item(movie.m_strFileNameAndPath,false);
8573 if (singleFiles && CUtil::SupportsWriteFileOperations(movie.m_strFileNameAndPath))
8575 if (!item.Exists(false))
8577 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, movie.m_strFileNameAndPath.c_str());
8582 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8584 if (overwrite || !CFile::Exists(nfoFile, false))
8586 if(!xmlDoc.SaveFile(nfoFile))
8588 CLog::Log(LOGERROR, "%s: Musicvideo nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8589 bSkip = ExportSkipEntry(nfoFile);
8606 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8607 xmlDoc.InsertEndChild(decl);
8609 if (images && !bSkip)
8613 CStdString strFileName(StringUtils::Join(movie.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + movie.m_strTitle);
8614 if (movie.m_iYear > 0)
8615 strFileName.AppendFormat("_%i", movie.m_iYear);
8616 item.SetPath(GetSafeFile(moviesDir, strFileName) + ".avi");
8618 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8620 CStdString savedThumb = item.GetLocalArt(i->first, false);
8621 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8629 // repeat for all tvshows
8630 sql = "SELECT * FROM tvshowview";
8631 m_pDS->query(sql.c_str());
8633 total = m_pDS->num_rows();
8636 while (!m_pDS->eof())
8638 CVideoInfoTag tvshow = GetDetailsForTvShow(m_pDS, true);
8640 map<int, map<string, string> > seasonArt;
8641 GetTvShowSeasonArt(tvshow.m_iDbId, seasonArt);
8643 map<string, string> artwork;
8644 if (GetArtForItem(tvshow.m_iDbId, tvshow.m_type, artwork) && !singleFiles)
8646 TiXmlElement additionalNode("art");
8647 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8648 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8649 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8651 TiXmlElement seasonNode("season");
8652 seasonNode.SetAttribute("num", i->first);
8653 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
8654 XMLUtils::SetString(&seasonNode, j->first.c_str(), j->second);
8655 additionalNode.InsertEndChild(seasonNode);
8657 tvshow.Save(pMain, "tvshow", true, &additionalNode);
8660 tvshow.Save(pMain, "tvshow", !singleFiles);
8662 // reset old skip state
8667 progress->SetLine(1, tvshow.m_strTitle);
8668 progress->SetPercentage(current * 100 / total);
8669 progress->Progress();
8670 if (progress->IsCanceled())
8678 // tvshow paths can be multipaths, and writing to a multipath is indeterminate.
8679 if (URIUtils::IsMultiPath(tvshow.m_strPath))
8680 tvshow.m_strPath = CMultiPathDirectory::GetFirstPath(tvshow.m_strPath);
8682 CFileItem item(tvshow.m_strPath, true);
8683 if (singleFiles && CUtil::SupportsWriteFileOperations(tvshow.m_strPath))
8685 if (!item.Exists(false))
8687 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, tvshow.m_strPath.c_str());
8692 CStdString nfoFile = URIUtils::AddFileToFolder(tvshow.m_strPath, "tvshow.nfo");
8694 if (overwrite || !CFile::Exists(nfoFile, false))
8696 if(!xmlDoc.SaveFile(nfoFile))
8698 CLog::Log(LOGERROR, "%s: TVShow nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8699 bSkip = ExportSkipEntry(nfoFile);
8716 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8717 xmlDoc.InsertEndChild(decl);
8719 if (images && !bSkip)
8722 item.SetPath(GetSafeFile(tvshowsDir, tvshow.m_strTitle));
8724 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8726 CStdString savedThumb = item.GetLocalArt(i->first, true);
8727 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8731 ExportActorThumbs(actorsDir, tvshow, singleFiles, overwrite);
8733 // export season thumbs
8734 for (map<int, map<string, string> >::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
8738 seasonThumb = "season-all";
8739 else if (i->first == 0)
8740 seasonThumb = "season-specials";
8742 seasonThumb = StringUtils::Format("season%02i", i->first);
8743 for (map<string, string>::const_iterator j = i->second.begin(); j != i->second.end(); j++)
8745 CStdString savedThumb(item.GetLocalArt(seasonThumb + "-" + j->first, true));
8746 if (!i->second.empty())
8747 CTextureCache::Get().Export(j->second, savedThumb, overwrite);
8752 // now save the episodes from this show
8753 sql = PrepareSQL("select * from episodeview where idShow=%i order by strFileName, idEpisode",tvshow.m_iDbId);
8754 pDS->query(sql.c_str());
8755 CStdString showDir(item.GetPath());
8759 CVideoInfoTag episode = GetDetailsForEpisode(pDS, true);
8760 map<string, string> artwork;
8761 if (GetArtForItem(episode.m_iDbId, "episode", artwork) && !singleFiles)
8763 TiXmlElement additionalNode("art");
8764 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8765 XMLUtils::SetString(&additionalNode, i->first.c_str(), i->second);
8766 episode.Save(pMain->LastChild(), "episodedetails", true, &additionalNode);
8768 else if (!singleFiles)
8769 episode.Save(pMain->LastChild(), "episodedetails", !singleFiles);
8771 episode.Save(pMain, "episodedetails", !singleFiles);
8773 // multi-episode files need dumping to the same XML
8774 while (singleFiles && !pDS->eof() &&
8775 episode.m_iFileId == pDS->fv("idFile").get_asInt())
8777 episode = GetDetailsForEpisode(pDS, true);
8778 episode.Save(pMain, "episodedetails", !singleFiles);
8782 // reset old skip state
8785 CFileItem item(episode.m_strFileNameAndPath, false);
8786 if (singleFiles && CUtil::SupportsWriteFileOperations(episode.m_strFileNameAndPath))
8788 if (!item.Exists(false))
8790 CLog::Log(LOGDEBUG, "%s - Not exporting item %s as it does not exist", __FUNCTION__, episode.m_strFileNameAndPath.c_str());
8795 CStdString nfoFile(URIUtils::ReplaceExtension(item.GetTBNFile(), ".nfo"));
8797 if (overwrite || !CFile::Exists(nfoFile, false))
8799 if(!xmlDoc.SaveFile(nfoFile))
8801 CLog::Log(LOGERROR, "%s: Episode nfo export failed! ('%s')", __FUNCTION__, nfoFile.c_str());
8802 bSkip = ExportSkipEntry(nfoFile);
8819 TiXmlDeclaration decl("1.0", "UTF-8", "yes");
8820 xmlDoc.InsertEndChild(decl);
8823 if (images && !bSkip)
8828 epName.Format("s%02ie%02i.avi", episode.m_iSeason, episode.m_iEpisode);
8829 item.SetPath(URIUtils::AddFileToFolder(showDir, epName));
8831 for (map<string, string>::const_iterator i = artwork.begin(); i != artwork.end(); ++i)
8833 CStdString savedThumb = item.GetLocalArt(i->first, false);
8834 CTextureCache::Get().Export(i->second, savedThumb, overwrite);
8837 ExportActorThumbs(actorsDir, episode, singleFiles, overwrite);
8846 if (singleFiles && progress)
8848 progress->SetPercentage(100);
8849 progress->Progress();
8854 // now dump path info
8855 set<CStdString> paths;
8857 TiXmlElement xmlPathElement("paths");
8858 TiXmlNode *pPaths = pMain->InsertEndChild(xmlPathElement);
8859 for( set<CStdString>::iterator iter = paths.begin(); iter != paths.end(); ++iter)
8861 bool foundDirectly = false;
8862 SScanSettings settings;
8863 ScraperPtr info = GetScraperForPath(*iter, settings, foundDirectly);
8864 if (info && foundDirectly)
8866 TiXmlElement xmlPathElement2("path");
8867 TiXmlNode *pPath = pPaths->InsertEndChild(xmlPathElement2);
8868 XMLUtils::SetString(pPath,"url", *iter);
8869 XMLUtils::SetInt(pPath,"scanrecursive", settings.recurse);
8870 XMLUtils::SetBoolean(pPath,"usefoldernames", settings.parent_name);
8871 XMLUtils::SetString(pPath,"content", TranslateContent(info->Content()));
8872 XMLUtils::SetString(pPath,"scraperpath", info->ID());
8875 xmlDoc.SaveFile(xmlFile);
8880 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
8887 void CVideoDatabase::ExportActorThumbs(const CStdString &strDir, const CVideoInfoTag &tag, bool singleFiles, bool overwrite /*=false*/)
8889 CStdString strPath(strDir);
8892 strPath = URIUtils::AddFileToFolder(tag.m_strPath, ".actors");
8893 if (!CDirectory::Exists(strPath))
8895 CDirectory::Create(strPath);
8896 CFile::SetHidden(strPath, true);
8900 for (CVideoInfoTag::iCast iter = tag.m_cast.begin();iter != tag.m_cast.end();++iter)
8903 item.SetLabel(iter->strName);
8904 if (!iter->thumb.IsEmpty())
8906 CStdString thumbFile(GetSafeFile(strPath, iter->strName));
8907 CTextureCache::Get().Export(iter->thumb, thumbFile, overwrite);
8912 bool CVideoDatabase::ExportSkipEntry(const CStdString &nfoFile)
8914 CStdString strParent;
8915 URIUtils::GetParentPath(nfoFile,strParent);
8916 CLog::Log(LOGERROR, "%s: Unable to write to '%s'!", __FUNCTION__, strParent.c_str());
8918 bool bSkip = CGUIDialogYesNo::ShowAndGetInput(g_localizeStrings.Get(647), g_localizeStrings.Get(20302), strParent.c_str(), g_localizeStrings.Get(20303));
8921 CLog::Log(LOGERROR, "%s: Skipping export of '%s' as requested", __FUNCTION__, nfoFile.c_str());
8923 CLog::Log(LOGERROR, "%s: Export failed! Canceling as requested", __FUNCTION__);
8928 void CVideoDatabase::ImportFromXML(const CStdString &path)
8930 CGUIDialogProgress *progress=NULL;
8933 if (NULL == m_pDB.get()) return;
8934 if (NULL == m_pDS.get()) return;
8936 CXBMCTinyXML xmlDoc;
8937 if (!xmlDoc.LoadFile(URIUtils::AddFileToFolder(path, "videodb.xml")))
8940 TiXmlElement *root = xmlDoc.RootElement();
8943 progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
8946 progress->SetHeading(648);
8947 progress->SetLine(0, 649);
8948 progress->SetLine(1, 330);
8949 progress->SetLine(2, "");
8950 progress->SetPercentage(0);
8951 progress->StartModal();
8952 progress->ShowProgressBar(true);
8956 XMLUtils::GetInt(root, "version", iVersion);
8958 CLog::Log(LOGDEBUG, "%s: Starting import (export version = %i)", __FUNCTION__, iVersion);
8960 TiXmlElement *movie = root->FirstChildElement();
8963 // first count the number of items...
8966 if (strnicmp(movie->Value(), "movie", 5)==0 ||
8967 strnicmp(movie->Value(), "tvshow", 6)==0 ||
8968 strnicmp(movie->Value(), "musicvideo",10)==0 )
8970 movie = movie->NextSiblingElement();
8973 CStdString actorsDir(URIUtils::AddFileToFolder(path, "actors"));
8974 CStdString moviesDir(URIUtils::AddFileToFolder(path, "movies"));
8975 CStdString musicvideosDir(URIUtils::AddFileToFolder(path, "musicvideos"));
8976 CStdString tvshowsDir(URIUtils::AddFileToFolder(path, "tvshows"));
8977 CVideoInfoScanner scanner;
8978 // add paths first (so we have scraper settings available)
8979 TiXmlElement *path = root->FirstChildElement("paths");
8980 path = path->FirstChildElement();
8984 if (XMLUtils::GetString(path,"url",strPath) && !strPath.empty())
8988 if (XMLUtils::GetString(path,"content", content) && !content.empty())
8989 { // check the scraper exists, if so store the path
8992 XMLUtils::GetString(path,"scraperpath",id);
8993 if (CAddonMgr::Get().GetAddon(id, addon))
8995 SScanSettings settings;
8996 ScraperPtr scraper = boost::dynamic_pointer_cast<CScraper>(addon);
8997 // FIXME: scraper settings are not exported?
8998 scraper->SetPathSettings(TranslateContent(content), "");
8999 XMLUtils::GetInt(path,"scanrecursive",settings.recurse);
9000 XMLUtils::GetBoolean(path,"usefoldernames",settings.parent_name);
9001 SetScraperForPath(strPath,scraper,settings);
9004 path = path->NextSiblingElement();
9006 movie = root->FirstChildElement();
9010 if (strnicmp(movie->Value(), "movie", 5) == 0)
9013 CFileItem item(info);
9014 bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.GetPath()) : false;
9015 CStdString filename = info.m_strTitle;
9016 if (info.m_iYear > 0)
9017 filename.AppendFormat("_%i", info.m_iYear);
9018 CFileItem artItem(item);
9019 artItem.SetPath(GetSafeFile(moviesDir, filename) + ".avi");
9020 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9021 item.SetArt(artItem.GetArt());
9022 scanner.AddVideo(&item, CONTENT_MOVIES, useFolders, true, NULL, true);
9025 else if (strnicmp(movie->Value(), "musicvideo", 10) == 0)
9028 CFileItem item(info);
9029 bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(item.GetPath()) : false;
9030 CStdString filename = StringUtils::Join(info.m_artist, g_advancedSettings.m_videoItemSeparator) + "." + info.m_strTitle;
9031 if (info.m_iYear > 0)
9032 filename.AppendFormat("_%i", info.m_iYear);
9033 CFileItem artItem(item);
9034 artItem.SetPath(GetSafeFile(musicvideosDir, filename) + ".avi");
9035 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9036 item.SetArt(artItem.GetArt());
9037 scanner.AddVideo(&item, CONTENT_MUSICVIDEOS, useFolders, true, NULL, true);
9040 else if (strnicmp(movie->Value(), "tvshow", 6) == 0)
9042 // load the TV show in. NOTE: This deletes all episodes under the TV Show, which may not be
9043 // what we desire. It may make better sense to only delete (or even better, update) the show information
9045 URIUtils::AddSlashAtEnd(info.m_strPath);
9046 DeleteTvShow(info.m_strPath);
9047 CFileItem showItem(info);
9048 bool useFolders = info.m_basePath.IsEmpty() ? LookupByFolders(showItem.GetPath(), true) : false;
9049 CFileItem artItem(showItem);
9050 CStdString artPath(GetSafeFile(tvshowsDir, info.m_strTitle));
9051 artItem.SetPath(artPath);
9052 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9053 showItem.SetArt(artItem.GetArt());
9054 int showID = scanner.AddVideo(&showItem, CONTENT_TVSHOWS, useFolders, true, NULL, true);
9056 map<int, map<string, string> > seasonArt;
9057 artItem.GetVideoInfoTag()->m_strPath = artPath;
9058 scanner.GetSeasonThumbs(*artItem.GetVideoInfoTag(), seasonArt, CVideoThumbLoader::GetArtTypes("season"), true);
9059 for (map<int, map<string, string> >::iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
9061 int seasonID = AddSeason(showID, i->first);
9062 SetArtForItem(seasonID, "season", i->second);
9065 // now load the episodes
9066 TiXmlElement *episode = movie->FirstChildElement("episodedetails");
9069 // no need to delete the episode info, due to the above deletion
9072 CFileItem item(info);
9073 CStdString filename;
9074 filename.Format("s%02ie%02i.avi", info.m_iSeason, info.m_iEpisode);
9075 CFileItem artItem(item);
9076 artItem.SetPath(GetSafeFile(artPath, filename));
9077 scanner.GetArtwork(&artItem, CONTENT_MOVIES, useFolders, true, actorsDir);
9078 item.SetArt(artItem.GetArt());
9079 scanner.AddVideo(&item,CONTENT_TVSHOWS, false, false, showItem.GetVideoInfoTag(), true);
9080 episode = episode->NextSiblingElement("episodedetails");
9083 movie = movie->NextSiblingElement();
9084 if (progress && total)
9086 progress->SetPercentage(current * 100 / total);
9087 progress->SetLine(2, info.m_strTitle);
9088 progress->Progress();
9089 if (progress->IsCanceled())
9092 RollbackTransaction();
9100 CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
9106 bool CVideoDatabase::ImportArtFromXML(const TiXmlNode *node, map<string, string> &artwork)
9108 if (!node) return false;
9109 const TiXmlNode *art = node->FirstChild();
9110 while (art && art->FirstChild())
9112 artwork.insert(make_pair(art->ValueStr(), art->FirstChild()->ValueStr()));
9113 art = art->NextSibling();
9115 return !artwork.empty();
9118 void CVideoDatabase::ConstructPath(CStdString& strDest, const CStdString& strPath, const CStdString& strFileName)
9120 if (URIUtils::IsStack(strFileName) ||
9121 URIUtils::IsInArchive(strFileName) || URIUtils::IsPlugin(strPath))
9122 strDest = strFileName;
9124 strDest = URIUtils::AddFileToFolder(strPath, strFileName);
9127 void CVideoDatabase::SplitPath(const CStdString& strFileNameAndPath, CStdString& strPath, CStdString& strFileName)
9129 if (URIUtils::IsStack(strFileNameAndPath) || strFileNameAndPath.Mid(0,6).Equals("rar://") || strFileNameAndPath.Mid(0,6).Equals("zip://"))
9131 URIUtils::GetParentPath(strFileNameAndPath,strPath);
9132 strFileName = strFileNameAndPath;
9134 else if (URIUtils::IsPlugin(strFileNameAndPath))
9136 CURL url(strFileNameAndPath);
9137 strPath = url.GetWithoutFilename();
9138 strFileName = strFileNameAndPath;
9141 URIUtils::Split(strFileNameAndPath,strPath, strFileName);
9144 void CVideoDatabase::InvalidatePathHash(const CStdString& strPath)
9146 SScanSettings settings;
9148 ScraperPtr info = GetScraperForPath(strPath,settings,foundDirectly);
9149 SetPathHash(strPath,"");
9152 if (info->Content() == CONTENT_TVSHOWS || (info->Content() == CONTENT_MOVIES && !foundDirectly)) // if we scan by folder name we need to invalidate parent as well
9154 if (info->Content() == CONTENT_TVSHOWS || settings.parent_name_root)
9156 CStdString strParent;
9157 URIUtils::GetParentPath(strPath,strParent);
9158 SetPathHash(strParent,"");
9163 bool CVideoDatabase::CommitTransaction()
9165 if (CDatabase::CommitTransaction())
9166 { // number of items in the db has likely changed, so recalculate
9167 g_infoManager.SetLibraryBool(LIBRARY_HAS_MOVIES, HasContent(VIDEODB_CONTENT_MOVIES));
9168 g_infoManager.SetLibraryBool(LIBRARY_HAS_TVSHOWS, HasContent(VIDEODB_CONTENT_TVSHOWS));
9169 g_infoManager.SetLibraryBool(LIBRARY_HAS_MUSICVIDEOS, HasContent(VIDEODB_CONTENT_MUSICVIDEOS));
9175 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, int dbField, const std::string &strValue)
9180 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9183 string strTable, strField;
9184 if (type == VIDEODB_CONTENT_MOVIES)
9187 strField = "idMovie";
9189 else if (type == VIDEODB_CONTENT_TVSHOWS)
9191 strTable = "tvshow";
9192 strField = "idShow";
9194 else if (type == VIDEODB_CONTENT_EPISODES)
9196 strTable = "episode";
9197 strField = "idEpisode";
9199 else if (type == VIDEODB_CONTENT_MUSICVIDEOS)
9201 strTable = "musicvideo";
9202 strField = "idMVideo";
9205 if (strTable.empty())
9208 return SetSingleValue(strTable, StringUtils::Format("c%02u", dbField), strValue, strField, dbId);
9212 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strSQL.c_str());
9217 bool CVideoDatabase::SetSingleValue(VIDEODB_CONTENT_TYPE type, int dbId, Field dbField, const std::string &strValue)
9219 MediaType mediaType = DatabaseUtils::MediaTypeFromVideoContentType(type);
9220 if (mediaType == MediaTypeNone)
9223 int dbFieldIndex = DatabaseUtils::GetField(dbField, mediaType);
9224 if (dbFieldIndex < 0)
9227 return SetSingleValue(type, dbId, dbFieldIndex, strValue);
9230 bool CVideoDatabase::SetSingleValue(const std::string &table, const std::string &fieldName, const std::string &strValue,
9231 const std::string &conditionName /* = "" */, int conditionValue /* = -1 */)
9233 if (table.empty() || fieldName.empty())
9239 if (NULL == m_pDB.get() || NULL == m_pDS.get())
9242 sql = PrepareSQL("UPDATE %s SET %s='%s'", table.c_str(), fieldName.c_str(), strValue.c_str());
9243 if (!conditionName.empty())
9244 sql += PrepareSQL(" WHERE %s=%u", conditionName.c_str(), conditionValue);
9245 if (m_pDS->exec(sql.c_str()) == 0)
9250 CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, sql.c_str());
9255 CStdString CVideoDatabase::GetSafeFile(const CStdString &dir, const CStdString &name) const
9257 CStdString safeThumb(name);
9258 safeThumb.Replace(' ', '_');
9259 return URIUtils::AddFileToFolder(dir, CUtil::MakeLegalFileName(safeThumb));
9262 void CVideoDatabase::AnnounceRemove(std::string content, int id)
9265 data["type"] = content;
9267 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnRemove", data);
9270 void CVideoDatabase::AnnounceUpdate(std::string content, int id)
9273 data["type"] = content;
9275 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::VideoLibrary, "xbmc", "OnUpdate", data);
9278 bool CVideoDatabase::GetItemsForPath(const CStdString &content, const CStdString &strPath, CFileItemList &items)
9280 CStdString path(strPath);
9282 if(URIUtils::IsMultiPath(path))
9284 vector<CStdString> paths;
9285 CMultiPathDirectory::GetPaths(path, paths);
9287 for(unsigned i=0;i<paths.size();i++)
9288 GetItemsForPath(content, paths[i], items);
9290 return items.Size() > 0;
9293 int pathID = GetPathId(path);
9297 if (content == "movies")
9299 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_PARENTPATHID, pathID));
9300 GetMoviesByWhere("videodb://movies/titles/", filter, items);
9302 else if (content == "episodes")
9304 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_EPISODE_PARENTPATHID, pathID));
9305 GetEpisodesByWhere("videodb://tvshows/titles/", filter, items);
9307 else if (content == "tvshows")
9309 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_TV_PARENTPATHID, pathID));
9310 GetTvShowsByWhere("videodb://tvshows/titles/", filter, items);
9312 else if (content == "musicvideos")
9314 Filter filter(PrepareSQL("c%02d=%d", VIDEODB_ID_MUSICVIDEO_PARENTPATHID, pathID));
9315 GetMusicVideosByWhere("videodb://musicvideos/titles/", filter, items);
9317 for (int i = 0; i < items.Size(); i++)
9318 items[i]->SetPath(items[i]->GetVideoInfoTag()->m_basePath);
9319 return items.Size() > 0;
9322 bool CVideoDatabase::GetFilter(CDbUrl &videoUrl, Filter &filter, SortDescription &sorting)
9324 if (!videoUrl.IsValid())
9327 std::string type = videoUrl.GetType();
9328 std::string itemType = ((const CVideoDbUrl &)videoUrl).GetItemType();
9329 const CUrlOptions::UrlOptions& options = videoUrl.GetOptions();
9330 CUrlOptions::UrlOptions::const_iterator option;
9332 if (type == "movies")
9334 option = options.find("genreid");
9335 if (option != options.end())
9337 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie"));
9338 filter.AppendWhere(PrepareSQL("genrelinkmovie.idGenre = %i", (int)option->second.asInteger()));
9341 option = options.find("genre");
9342 if (option != options.end())
9344 filter.AppendJoin(PrepareSQL("join genrelinkmovie on genrelinkmovie.idMovie = movieview.idMovie join genre on genre.idGenre = genrelinkmovie.idGenre"));
9345 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9348 option = options.find("countryid");
9349 if (option != options.end())
9351 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie"));
9352 filter.AppendWhere(PrepareSQL("countrylinkmovie.idCountry = %i", (int)option->second.asInteger()));
9355 option = options.find("country");
9356 if (option != options.end())
9358 filter.AppendJoin(PrepareSQL("join countrylinkmovie on countrylinkmovie.idMovie = movieview.idMovie join country on country.idCountry = countrylinkmovie.idCountry"));
9359 filter.AppendWhere(PrepareSQL("country.strCountry like '%s'", option->second.asString().c_str()));
9362 option = options.find("studioid");
9363 if (option != options.end())
9365 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie"));
9366 filter.AppendWhere(PrepareSQL("studiolinkmovie.idStudio = %i", (int)option->second.asInteger()));
9369 option = options.find("studio");
9370 if (option != options.end())
9372 filter.AppendJoin(PrepareSQL("join studiolinkmovie on studiolinkmovie.idMovie = movieview.idMovie join studio on studio.idStudio = studiolinkmovie.idStudio"));
9373 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9376 option = options.find("directorid");
9377 if (option != options.end())
9379 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie"));
9380 filter.AppendWhere(PrepareSQL("directorlinkmovie.idDirector = %i", (int)option->second.asInteger()));
9383 option = options.find("director");
9384 if (option != options.end())
9386 filter.AppendJoin(PrepareSQL("join directorlinkmovie on directorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = directorlinkmovie.idDirector"));
9387 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9390 option = options.find("year");
9391 if (option != options.end())
9392 filter.AppendWhere(PrepareSQL("movieview.c%02d = '%i'", VIDEODB_ID_YEAR, (int)option->second.asInteger()));
9394 option = options.find("actorid");
9395 if (option != options.end())
9397 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie"));
9398 filter.AppendWhere(PrepareSQL("actorlinkmovie.idActor = %i", (int)option->second.asInteger()));
9401 option = options.find("actor");
9402 if (option != options.end())
9404 filter.AppendJoin(PrepareSQL("join actorlinkmovie on actorlinkmovie.idMovie = movieview.idMovie join actors on actors.idActor = actorlinkmovie.idActor"));
9405 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9408 option = options.find("setid");
9409 if (option != options.end())
9410 filter.AppendWhere(PrepareSQL("movieview.idSet = %i", (int)option->second.asInteger()));
9412 option = options.find("set");
9413 if (option != options.end())
9415 filter.AppendJoin(PrepareSQL("join setlinkmovie on setlinkmovie.idMovie = movieview.idMovie join sets on sets.idSet = setlinkmovie.idSet"));
9416 filter.AppendWhere(PrepareSQL("sets.strSet like '%s'", option->second.asString().c_str()));
9419 option = options.find("tagid");
9420 if (option != options.end())
9422 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie'"));
9423 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9426 option = options.find("tag");
9427 if (option != options.end())
9429 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = movieview.idMovie AND taglinks.media_type = 'movie' join tag on tag.idTag = taglinks.idTag"));
9430 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9433 else if (type == "tvshows")
9435 if (itemType == "tvshows")
9437 option = options.find("genreid");
9438 if (option != options.end())
9440 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9441 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9444 option = options.find("genre");
9445 if (option != options.end())
9447 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9448 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9451 option = options.find("studioid");
9452 if (option != options.end())
9454 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow"));
9455 filter.AppendWhere(PrepareSQL("studiolinktvshow.idStudio = %i", (int)option->second.asInteger()));
9458 option = options.find("studio");
9459 if (option != options.end())
9461 filter.AppendJoin(PrepareSQL("join studiolinktvshow on studiolinktvshow.idShow = tvshowview.idShow join studio on studio.idStudio = studiolinktvshow.idStudio"));
9462 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9465 option = options.find("directorid");
9466 if (option != options.end())
9468 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9469 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9472 option = options.find("year");
9473 if (option != options.end())
9474 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9476 option = options.find("actorid");
9477 if (option != options.end())
9479 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9480 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9483 option = options.find("actor");
9484 if (option != options.end())
9486 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9487 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9490 option = options.find("tagid");
9491 if (option != options.end())
9493 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow'"));
9494 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9497 option = options.find("tag");
9498 if (option != options.end())
9500 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = tvshowview.idShow AND taglinks.media_type = 'tvshow' join tag on tag.idTag = taglinks.idTag"));
9501 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9504 else if (itemType == "seasons")
9506 option = options.find("genreid");
9507 if (option != options.end())
9509 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = tvshowview.idShow"));
9510 filter.AppendWhere(PrepareSQL("genrelinktvshow.idGenre = %i", (int)option->second.asInteger()));
9513 option = options.find("directorid");
9514 if (option != options.end())
9516 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = tvshowview.idShow"));
9517 filter.AppendWhere(PrepareSQL("directorlinktvshow.idDirector = %i", (int)option->second.asInteger()));
9520 option = options.find("year");
9521 if (option != options.end())
9522 filter.AppendWhere(PrepareSQL("tvshowview.c%02d like '%%%i%%'", VIDEODB_ID_TV_PREMIERED, (int)option->second.asInteger()));
9524 option = options.find("actorid");
9525 if (option != options.end())
9527 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = tvshowview.idShow"));
9528 filter.AppendWhere(PrepareSQL("actorlinktvshow.idActor = %i", (int)option->second.asInteger()));
9531 else if (itemType == "episodes")
9534 option = options.find("tvshowid");
9535 if (option != options.end())
9536 idShow = (int)option->second.asInteger();
9539 option = options.find("season");
9540 if (option != options.end())
9541 season = (int)option->second.asInteger();
9543 CStdString strIn = PrepareSQL("= %i", idShow);
9544 GetStackedTvShowList(idShow, strIn);
9548 bool condition = false;
9550 option = options.find("genreid");
9551 if (option != options.end())
9554 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow"));
9555 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genrelinktvshow.idGenre = %i", idShow, (int)option->second.asInteger()));
9558 option = options.find("genre");
9559 if (option != options.end())
9562 filter.AppendJoin(PrepareSQL("join genrelinktvshow on genrelinktvshow.idShow = episodeview.idShow join genre on genre.idGenre = genrelinktvshow.idGenre"));
9563 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and genre.strGenre like '%s'", idShow, option->second.asString().c_str()));
9566 option = options.find("directorid");
9567 if (option != options.end())
9570 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow"));
9571 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and directorlinktvshow.idDirector = %i", idShow, (int)option->second.asInteger()));
9574 option = options.find("director");
9575 if (option != options.end())
9578 filter.AppendJoin(PrepareSQL("join directorlinktvshow on directorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = directorlinktvshow.idDirector"));
9579 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor like '%s'", idShow, option->second.asString().c_str()));
9582 option = options.find("year");
9583 if (option != options.end())
9586 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and episodeview.premiered like '%%%i%%'", idShow, (int)option->second.asInteger()));
9589 option = options.find("actorid");
9590 if (option != options.end())
9593 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow"));
9594 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actorlinktvshow.idActor = %i", idShow, (int)option->second.asInteger()));
9597 option = options.find("actor");
9598 if (option != options.end())
9601 filter.AppendJoin(PrepareSQL("join actorlinktvshow on actorlinktvshow.idShow = episodeview.idShow join actors on actors.idActor = actorlinktvshow.idActor"));
9602 filter.AppendWhere(PrepareSQL("episodeview.idShow = %i and actors.strActor = '%s'", idShow, option->second.asString().c_str()));
9606 filter.AppendWhere(PrepareSQL("episodeview.idShow %s", strIn.c_str()));
9610 if (season == 0) // season = 0 indicates a special - we grab all specials here (see below)
9611 filter.AppendWhere(PrepareSQL("episodeview.c%02d = %i", VIDEODB_ID_EPISODE_SEASON, season));
9613 filter.AppendWhere(PrepareSQL("(episodeview.c%02d = %i or (episodeview.c%02d = 0 and (episodeview.c%02d = 0 or episodeview.c%02d = %i)))",
9614 VIDEODB_ID_EPISODE_SEASON, season, VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_SORTSEASON, VIDEODB_ID_EPISODE_SORTSEASON, season));
9619 option = options.find("year");
9620 if (option != options.end())
9621 filter.AppendWhere(PrepareSQL("episodeview.premiered like '%%%i%%'", (int)option->second.asInteger()));
9623 option = options.find("directorid");
9624 if (option != options.end())
9626 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode"));
9627 filter.AppendWhere(PrepareSQL("directorlinkepisode.idDirector = %i", (int)option->second.asInteger()));
9630 option = options.find("director");
9631 if (option != options.end())
9633 filter.AppendJoin(PrepareSQL("join directorlinkepisode on directorlinkepisode.idEpisode = episodeview.idEpisode join actors on actors.idActor = directorlinktvshow.idDirector"));
9634 filter.AppendWhere(PrepareSQL("actors.strActor = %s", option->second.asString().c_str()));
9639 else if (type == "musicvideos")
9641 option = options.find("genreid");
9642 if (option != options.end())
9644 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9645 filter.AppendWhere(PrepareSQL("genrelinkmusicvideo.idGenre = %i", (int)option->second.asInteger()));
9648 option = options.find("genre");
9649 if (option != options.end())
9651 filter.AppendJoin(PrepareSQL("join genrelinkmusicvideo on genrelinkmusicvideo.idMVideo = musicvideoview.idMVideo join genre on genre.idGenre = genrelinkmusicvideo.idGenre"));
9652 filter.AppendWhere(PrepareSQL("genre.strGenre like '%s'", option->second.asString().c_str()));
9655 option = options.find("studioid");
9656 if (option != options.end())
9658 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9659 filter.AppendWhere(PrepareSQL("studiolinkmusicvideo.idStudio = %i", (int)option->second.asInteger()));
9662 option = options.find("studio");
9663 if (option != options.end())
9665 filter.AppendJoin(PrepareSQL("join studiolinkmusicvideo on studiolinkmusicvideo.idMVideo = musicvideoview.idMVideo join studio on studio.idStudio = studiolinkmusicvideo.idStudio"));
9666 filter.AppendWhere(PrepareSQL("studio.strStudio like '%s'", option->second.asString().c_str()));
9669 option = options.find("directorid");
9670 if (option != options.end())
9672 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9673 filter.AppendWhere(PrepareSQL("directorlinkmusicvideo.idDirector = %i", (int)option->second.asInteger()));
9676 option = options.find("director");
9677 if (option != options.end())
9679 filter.AppendJoin(PrepareSQL("join directorlinkmusicvideo on directorlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = directorlinkmusicvideo.idDirector"));
9680 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9683 option = options.find("year");
9684 if (option != options.end())
9685 filter.AppendWhere(PrepareSQL("musicvideoview.c%02d = '%i'",VIDEODB_ID_MUSICVIDEO_YEAR, (int)option->second.asInteger()));
9687 option = options.find("artistid");
9688 if (option != options.end())
9690 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo"));
9691 filter.AppendWhere(PrepareSQL("artistlinkmusicvideo.idArtist = %i", (int)option->second.asInteger()));
9694 option = options.find("artist");
9695 if (option != options.end())
9697 filter.AppendJoin(PrepareSQL("join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo = musicvideoview.idMVideo join actors on actors.idActor = artistlinkmusicvideo.idArtist"));
9698 filter.AppendWhere(PrepareSQL("actors.strActor like '%s'", option->second.asString().c_str()));
9701 option = options.find("albumid");
9702 if (option != options.end())
9703 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()));
9705 option = options.find("tagid");
9706 if (option != options.end())
9708 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo'"));
9709 filter.AppendWhere(PrepareSQL("taglinks.idTag = %i", (int)option->second.asInteger()));
9712 option = options.find("tag");
9713 if (option != options.end())
9715 filter.AppendJoin(PrepareSQL("join taglinks on taglinks.idMedia = musicvideoview.idMVideo AND taglinks.media_type = 'musicvideo' join tag on tag.idTag = taglinks.idTag"));
9716 filter.AppendWhere(PrepareSQL("tag.strTag like '%s'", option->second.asString().c_str()));
9722 option = options.find("xsp");
9723 if (option != options.end())
9726 if (!xsp.LoadFromJson(option->second.asString()))
9729 // check if the filter playlist matches the item type
9730 if (xsp.GetType() == itemType ||
9731 (xsp.GetGroup() == itemType && !xsp.IsGroupMixed()) ||
9732 // handle episode listings with videodb://tvshows/titles/ which get the rest
9733 // of the path (season and episodeid) appended later
9734 (xsp.GetType() == "episodes" && itemType == "tvshows"))
9736 std::set<CStdString> playlists;
9737 filter.AppendWhere(xsp.GetWhereClause(*this, playlists));
9739 if (xsp.GetLimit() > 0)
9740 sorting.limitEnd = xsp.GetLimit();
9741 if (xsp.GetOrder() != SortByNone)
9742 sorting.sortBy = xsp.GetOrder();
9743 if (xsp.GetOrderDirection() != SortOrderNone)
9744 sorting.sortOrder = xsp.GetOrderDirection();
9745 if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
9746 sorting.sortAttributes = SortAttributeIgnoreArticle;
9750 option = options.find("filter");
9751 if (option != options.end())
9753 CSmartPlaylist xspFilter;
9754 if (!xspFilter.LoadFromJson(option->second.asString()))
9757 // check if the filter playlist matches the item type
9758 if (xspFilter.GetType() == itemType)
9760 std::set<CStdString> playlists;
9761 filter.AppendWhere(xspFilter.GetWhereClause(*this, playlists));
9763 // remove the filter if it doesn't match the item type
9765 videoUrl.RemoveOption("filter");