Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: 392bf9ce71
Fetching contributors…

Cannot retrieve contributors at this time

8494 lines (7518 sloc) 318.577 kb
/*
* Copyright (C) 2005-2008 Team XBMC
* http://www.xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
* http://www.gnu.org/copyleft/gpl.html
*
*/
#include "threads/SystemClock.h"
#include "VideoDatabase.h"
#include "video/windows/GUIWindowVideoBase.h"
#include "utils/RegExp.h"
#include "addons/AddonManager.h"
#include "GUIInfoManager.h"
#include "Util.h"
#include "utils/URIUtils.h"
#include "utils/XMLUtils.h"
#include "GUIPassword.h"
#include "filesystem/StackDirectory.h"
#include "filesystem/MultiPathDirectory.h"
#include "VideoInfoScanner.h"
#include "guilib/GUIWindowManager.h"
#include "filesystem/Directory.h"
#include "filesystem/File.h"
#include "filesystem/SpecialProtocol.h"
#include "dialogs/GUIDialogProgress.h"
#include "dialogs/GUIDialogYesNo.h"
#include "FileItem.h"
#include "settings/AdvancedSettings.h"
#include "settings/GUISettings.h"
#include "settings/Settings.h"
#include "utils/StringUtils.h"
#include "guilib/LocalizeStrings.h"
#include "utils/TimeUtils.h"
#include "utils/log.h"
#include "TextureCache.h"
#include "addons/AddonInstaller.h"
#include "interfaces/AnnouncementManager.h"
#include "dbwrappers/dataset.h"
#include "utils/LabelFormatter.h"
#include "XBDateTime.h"
using namespace std;
using namespace dbiplus;
using namespace XFILE;
using namespace VIDEO;
using namespace ADDON;
//********************************************************************************************************************************
CVideoDatabase::CVideoDatabase(void)
{
}
//********************************************************************************************************************************
CVideoDatabase::~CVideoDatabase(void)
{}
//********************************************************************************************************************************
bool CVideoDatabase::Open()
{
return CDatabase::Open(g_advancedSettings.m_databaseVideo);
}
bool CVideoDatabase::CreateTables()
{
/* indexes should be added on any columns that are used in in */
/* a where or a join. primary key on a column is the same as a */
/* unique index on that column, so there is no need to add any */
/* index if no other columns are refered */
/* order of indexes are important, for an index to be considered all */
/* columns up to the column in question have to have been specified */
/* select * from actorlinkmovie where idMovie = 1, can not take */
/* advantage of a index that has been created on ( idGenre, idMovie ) */
/*, hower on on ( idMovie, idGenre ) will be considered for use */
BeginTransaction();
try
{
CDatabase::CreateTables();
CLog::Log(LOGINFO, "create bookmark table");
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");
m_pDS->exec("CREATE INDEX ix_bookmark ON bookmark (idFile, type)");
CLog::Log(LOGINFO, "create settings table");
m_pDS->exec("CREATE TABLE settings ( idFile integer, Deinterlace bool,"
"ViewMode integer,ZoomAmount float, PixelRatio float, VerticalShift float, AudioStream integer, SubtitleStream integer,"
"SubtitleDelay float, SubtitlesOn bool, Brightness float, Contrast float, Gamma float,"
"VolumeAmplification float, AudioDelay float, OutputToAllSpeakers bool, ResumeTime integer, Crop bool, CropLeft integer,"
"CropRight integer, CropTop integer, CropBottom integer, Sharpness float, NoiseReduction float, NonLinStretch bool, PostProcess bool,"
"ScalingMethod integer, DeinterlaceMode integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_settings ON settings ( idFile )\n");
CLog::Log(LOGINFO, "create stacktimes table");
m_pDS->exec("CREATE TABLE stacktimes (idFile integer, times text)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_stacktimes ON stacktimes ( idFile )\n");
CLog::Log(LOGINFO, "create genre table");
m_pDS->exec("CREATE TABLE genre ( idGenre integer primary key, strGenre text)\n");
CLog::Log(LOGINFO, "create genrelinkmovie table");
m_pDS->exec("CREATE TABLE genrelinkmovie ( idGenre integer, idMovie integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_1 ON genrelinkmovie ( idGenre, idMovie)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmovie_2 ON genrelinkmovie ( idMovie, idGenre)\n");
CLog::Log(LOGINFO, "create country table");
m_pDS->exec("CREATE TABLE country ( idCountry integer primary key, strCountry text)\n");
CLog::Log(LOGINFO, "create countrylinkmovie table");
m_pDS->exec("CREATE TABLE countrylinkmovie ( idCountry integer, idMovie integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_1 ON countrylinkmovie ( idCountry, idMovie)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_countrylinkmovie_2 ON countrylinkmovie ( idMovie, idCountry)\n");
CLog::Log(LOGINFO, "create movie table");
CStdString columns = "CREATE TABLE movie ( idMovie integer primary key, idFile integer";
for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
{
CStdString column;
column.Format(",c%02d text", i);
columns += column;
}
columns += ")";
m_pDS->exec(columns.c_str());
m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_1 ON movie (idFile, idMovie)");
m_pDS->exec("CREATE UNIQUE INDEX ix_movie_file_2 ON movie (idMovie, idFile)");
CLog::Log(LOGINFO, "create actorlinkmovie table");
m_pDS->exec("CREATE TABLE actorlinkmovie ( idActor integer, idMovie integer, strRole text, iOrder integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_1 ON actorlinkmovie ( idActor, idMovie )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkmovie_2 ON actorlinkmovie ( idMovie, idActor )\n");
CLog::Log(LOGINFO, "create directorlinkmovie table");
m_pDS->exec("CREATE TABLE directorlinkmovie ( idDirector integer, idMovie integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_1 ON directorlinkmovie ( idDirector, idMovie )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmovie_2 ON directorlinkmovie ( idMovie, idDirector )\n");
CLog::Log(LOGINFO, "create writerlinkmovie table");
m_pDS->exec("CREATE TABLE writerlinkmovie ( idWriter integer, idMovie integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_1 ON writerlinkmovie ( idWriter, idMovie )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkmovie_2 ON writerlinkmovie ( idMovie, idWriter )\n");
CLog::Log(LOGINFO, "create actors table");
m_pDS->exec("CREATE TABLE actors ( idActor integer primary key, strActor text, strThumb text )\n");
CLog::Log(LOGINFO, "create path table");
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)");
m_pDS->exec("CREATE UNIQUE INDEX ix_path ON path ( strPath(255) )");
CLog::Log(LOGINFO, "create files table");
m_pDS->exec("CREATE TABLE files ( idFile integer primary key, idPath integer, strFilename text, playCount integer, lastPlayed text, dateAdded text)");
m_pDS->exec("CREATE UNIQUE INDEX ix_files ON files ( idPath, strFilename(255) )");
CLog::Log(LOGINFO, "create tvshow table");
columns = "CREATE TABLE tvshow ( idShow integer primary key";
for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
{
CStdString column;
column.Format(",c%02d text", i);
columns += column;
}
columns += ")";
m_pDS->exec(columns.c_str());
CLog::Log(LOGINFO, "create directorlinktvshow table");
m_pDS->exec("CREATE TABLE directorlinktvshow ( idDirector integer, idShow integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_1 ON directorlinktvshow ( idDirector, idShow )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinktvshow_2 ON directorlinktvshow ( idShow, idDirector )\n");
CLog::Log(LOGINFO, "create actorlinktvshow table");
m_pDS->exec("CREATE TABLE actorlinktvshow ( idActor integer, idShow integer, strRole text, iOrder integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_1 ON actorlinktvshow ( idActor, idShow )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinktvshow_2 ON actorlinktvshow ( idShow, idActor )\n");
CLog::Log(LOGINFO, "create studiolinktvshow table");
m_pDS->exec("CREATE TABLE studiolinktvshow ( idStudio integer, idShow integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_1 ON studiolinktvshow ( idStudio, idShow)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinktvshow_2 ON studiolinktvshow ( idShow, idStudio)\n");
CLog::Log(LOGINFO, "create episode table");
columns = "CREATE TABLE episode ( idEpisode integer primary key, idFile integer";
for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
{
CStdString column;
if ( i == VIDEODB_ID_EPISODE_SEASON || i == VIDEODB_ID_EPISODE_EPISODE || i == VIDEODB_ID_EPISODE_BOOKMARK)
column.Format(",c%02d varchar(24)", i);
else
column.Format(",c%02d text", i);
columns += column;
}
columns += ", idShow integer)";
m_pDS->exec(columns.c_str());
m_pDS->exec("CREATE UNIQUE INDEX ix_episode_file_1 on episode (idEpisode, idFile)");
m_pDS->exec("CREATE UNIQUE INDEX id_episode_file_2 on episode (idFile, idEpisode)");
CStdString createColIndex;
createColIndex.Format("CREATE INDEX ix_episode_season_episode on episode (c%02d, c%02d)", VIDEODB_ID_EPISODE_SEASON, VIDEODB_ID_EPISODE_EPISODE);
m_pDS->exec(createColIndex.c_str());
createColIndex.Format("CREATE INDEX ix_episode_bookmark on episode (c%02d)", VIDEODB_ID_EPISODE_BOOKMARK);
m_pDS->exec(createColIndex.c_str());
m_pDS->exec("CREATE INDEX ix_episode_show1 on episode(idEpisode,idShow)");
m_pDS->exec("CREATE INDEX ix_episode_show2 on episode(idShow,idEpisode)");
CLog::Log(LOGINFO, "create tvshowlinkpath table");
m_pDS->exec("CREATE TABLE tvshowlinkpath (idShow integer, idPath integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_1 ON tvshowlinkpath ( idShow, idPath )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_tvshowlinkpath_2 ON tvshowlinkpath ( idPath, idShow )\n");
CLog::Log(LOGINFO, "create actorlinkepisode table");
m_pDS->exec("CREATE TABLE actorlinkepisode ( idActor integer, idEpisode integer, strRole text, iOrder integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_1 ON actorlinkepisode ( idActor, idEpisode )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_actorlinkepisode_2 ON actorlinkepisode ( idEpisode, idActor )\n");
CLog::Log(LOGINFO, "create directorlinkepisode table");
m_pDS->exec("CREATE TABLE directorlinkepisode ( idDirector integer, idEpisode integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_1 ON directorlinkepisode ( idDirector, idEpisode )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkepisode_2 ON directorlinkepisode ( idEpisode, idDirector )\n");
CLog::Log(LOGINFO, "create writerlinkepisode table");
m_pDS->exec("CREATE TABLE writerlinkepisode ( idWriter integer, idEpisode integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_1 ON writerlinkepisode ( idWriter, idEpisode )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_writerlinkepisode_2 ON writerlinkepisode ( idEpisode, idWriter )\n");
CLog::Log(LOGINFO, "create genrelinktvshow table");
m_pDS->exec("CREATE TABLE genrelinktvshow ( idGenre integer, idShow integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_1 ON genrelinktvshow ( idGenre, idShow)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinktvshow_2 ON genrelinktvshow ( idShow, idGenre)\n");
CLog::Log(LOGINFO, "create movielinktvshow table");
m_pDS->exec("CREATE TABLE movielinktvshow ( idMovie integer, IdShow integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_1 ON movielinktvshow ( idShow, idMovie)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_movielinktvshow_2 ON movielinktvshow ( idMovie, idShow)\n");
CLog::Log(LOGINFO, "create studio table");
m_pDS->exec("CREATE TABLE studio ( idStudio integer primary key, strStudio text)\n");
CLog::Log(LOGINFO, "create studiolinkmovie table");
m_pDS->exec("CREATE TABLE studiolinkmovie ( idStudio integer, idMovie integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_1 ON studiolinkmovie ( idStudio, idMovie)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmovie_2 ON studiolinkmovie ( idMovie, idStudio)\n");
CLog::Log(LOGINFO, "create musicvideo table");
columns = "CREATE TABLE musicvideo ( idMVideo integer primary key, idFile integer";
for (int i = 0; i < VIDEODB_MAX_COLUMNS; i++)
{
CStdString column;
column.Format(",c%02d text", i);
columns += column;
}
columns += ")";
m_pDS->exec(columns.c_str());
m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_1 on musicvideo (idMVideo, idFile)");
m_pDS->exec("CREATE UNIQUE INDEX ix_musicvideo_file_2 on musicvideo (idFile, idMVideo)");
CLog::Log(LOGINFO, "create artistlinkmusicvideo table");
m_pDS->exec("CREATE TABLE artistlinkmusicvideo ( idArtist integer, idMVideo integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_1 ON artistlinkmusicvideo ( idArtist, idMVideo)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_artistlinkmusicvideo_2 ON artistlinkmusicvideo ( idMVideo, idArtist)\n");
CLog::Log(LOGINFO, "create genrelinkmusicvideo table");
m_pDS->exec("CREATE TABLE genrelinkmusicvideo ( idGenre integer, idMVideo integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_1 ON genrelinkmusicvideo ( idGenre, idMVideo)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_genrelinkmusicvideo_2 ON genrelinkmusicvideo ( idMVideo, idGenre)\n");
CLog::Log(LOGINFO, "create studiolinkmusicvideo table");
m_pDS->exec("CREATE TABLE studiolinkmusicvideo ( idStudio integer, idMVideo integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_1 ON studiolinkmusicvideo ( idStudio, idMVideo)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_studiolinkmusicvideo_2 ON studiolinkmusicvideo ( idMVideo, idStudio)\n");
CLog::Log(LOGINFO, "create directorlinkmusicvideo table");
m_pDS->exec("CREATE TABLE directorlinkmusicvideo ( idDirector integer, idMVideo integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_1 ON directorlinkmusicvideo ( idDirector, idMVideo )\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_directorlinkmusicvideo_2 ON directorlinkmusicvideo ( idMVideo, idDirector )\n");
CLog::Log(LOGINFO, "create streaminfo table");
m_pDS->exec("CREATE TABLE streamdetails (idFile integer, iStreamType integer, "
"strVideoCodec text, fVideoAspect float, iVideoWidth integer, iVideoHeight integer, "
"strAudioCodec text, iAudioChannels integer, strAudioLanguage text, strSubtitleLanguage text, iVideoDuration integer)");
m_pDS->exec("CREATE INDEX ix_streamdetails ON streamdetails (idFile)");
CLog::Log(LOGINFO, "create sets table");
m_pDS->exec("CREATE TABLE sets ( idSet integer primary key, strSet text)\n");
CLog::Log(LOGINFO, "create setlinkmovie table");
m_pDS->exec("CREATE TABLE setlinkmovie ( idSet integer, idMovie integer)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_setlinkmovie_1 ON setlinkmovie ( idSet, idMovie)\n");
m_pDS->exec("CREATE UNIQUE INDEX ix_setlinkmovie_2 ON setlinkmovie ( idMovie, idSet)\n");
// create basepath indices
m_pDS->exec("CREATE INDEX ixMovieBasePath ON movie ( c23(12) )");
m_pDS->exec("CREATE INDEX ixMusicVideoBasePath ON musicvideo ( c14(12) )");
m_pDS->exec("CREATE INDEX ixEpisodeBasePath ON episode ( c19(12) )");
m_pDS->exec("CREATE INDEX ixTVShowBasePath on tvshow ( c17(12) )");
CLog::Log(LOGINFO, "create seasons table");
m_pDS->exec("CREATE TABLE seasons ( idSeason integer primary key, idShow integer, season integer)");
m_pDS->exec("CREATE INDEX ix_seasons ON seasons (idShow, season)");
CLog::Log(LOGINFO, "create art table and triggers");
m_pDS->exec("CREATE TABLE art(art_id INTEGER PRIMARY KEY, media_id INTEGER, media_type TEXT, type TEXT, url TEXT)");
m_pDS->exec("CREATE INDEX ix_art ON art(media_id, media_type(20), type(20))");
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");
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");
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");
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");
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");
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");
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");
// we create views last to ensure all indexes are rolled in
CreateViews();
}
catch (...)
{
CLog::Log(LOGERROR, "%s unable to create tables:%i", __FUNCTION__, (int)GetLastError());
RollbackTransaction();
return false;
}
CommitTransaction();
return true;
}
void CVideoDatabase::CreateViews()
{
CLog::Log(LOGINFO, "create episodeview");
m_pDS->exec("DROP VIEW IF EXISTS episodeview");
CStdString episodeview = PrepareSQL("CREATE VIEW episodeview AS SELECT "
" episode.*,"
" files.strFileName AS strFileName,"
" path.strPath AS strPath,"
" files.playCount AS playCount,"
" files.lastPlayed AS lastPlayed,"
" files.dateAdded AS dateAdded,"
" tvshow.c%02d AS strTitle,"
" tvshow.c%02d AS strStudio,"
" tvshow.c%02d AS premiered,"
" tvshow.c%02d AS mpaa,"
" tvshow.c%02d AS strShowPath "
"FROM episode"
" JOIN files ON"
" files.idFile=episode.idFile"
" JOIN tvshow ON"
" tvshow.idShow=episode.idShow"
" JOIN path ON"
" files.idPath=path.idPath", VIDEODB_ID_TV_TITLE, VIDEODB_ID_TV_STUDIOS, VIDEODB_ID_TV_PREMIERED, VIDEODB_ID_TV_MPAA, VIDEODB_ID_TV_BASEPATH);
m_pDS->exec(episodeview.c_str());
CLog::Log(LOGINFO, "create tvshowview");
m_pDS->exec("DROP VIEW IF EXISTS tvshowview");
CStdString tvshowview = PrepareSQL("CREATE VIEW tvshowview AS SELECT "
" tvshow.*,"
" path.strPath AS strPath,"
" path.dateAdded AS dateAdded,"
" NULLIF(COUNT(episode.c12), 0) AS totalCount,"
" COUNT(files.playCount) AS watchedcount,"
" NULLIF(COUNT(DISTINCT(episode.c12)), 0) AS totalSeasons "
"FROM tvshow"
" LEFT JOIN tvshowlinkpath ON"
" tvshowlinkpath.idShow=tvshow.idShow"
" LEFT JOIN path ON"
" path.idPath=tvshowlinkpath.idPath"
" LEFT JOIN episode ON"
" episode.idShow=tvshow.idShow"
" LEFT JOIN files ON"
" files.idFile=episode.idFile "
"GROUP BY tvshow.idShow;");
m_pDS->exec(tvshowview.c_str());
CLog::Log(LOGINFO, "create musicvideoview");
m_pDS->exec("DROP VIEW IF EXISTS musicvideoview");
m_pDS->exec("CREATE VIEW musicvideoview AS SELECT"
" musicvideo.*,"
" files.strFileName as strFileName,"
" path.strPath as strPath,"
" files.playCount as playCount,"
" files.lastPlayed as lastPlayed,"
" files.dateAdded as dateAdded "
"FROM musicvideo"
" JOIN files ON"
" files.idFile=musicvideo.idFile"
" JOIN path ON"
" path.idPath=files.idPath");
CLog::Log(LOGINFO, "create movieview");
m_pDS->exec("DROP VIEW IF EXISTS movieview");
m_pDS->exec("CREATE VIEW movieview AS SELECT"
" movie.*,"
" files.strFileName AS strFileName,"
" path.strPath AS strPath,"
" files.playCount AS playCount,"
" files.lastPlayed AS lastPlayed, "
" files.dateAdded AS dateAdded "
"FROM movie"
" JOIN files ON"
" files.idFile=movie.idFile"
" JOIN path ON"
" path.idPath=files.idPath");
}
//********************************************************************************************************************************
int CVideoDatabase::GetPathId(const CStdString& strPath)
{
CStdString strSQL;
try
{
int idPath=-1;
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
CStdString strPath1(strPath);
if (URIUtils::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
URIUtils::GetParentPath(strPath,strPath1);
URIUtils::AddSlashAtEnd(strPath1);
strSQL=PrepareSQL("select idPath from path where strPath='%s'",strPath1.c_str());
m_pDS->query(strSQL.c_str());
if (!m_pDS->eof())
idPath = m_pDS->fv("path.idPath").get_asInt();
m_pDS->close();
return idPath;
}
catch (...)
{
CLog::Log(LOGERROR, "%s unable to getpath (%s)", __FUNCTION__, strSQL.c_str());
}
return -1;
}
bool CVideoDatabase::GetPaths(set<CStdString> &paths)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
paths.clear();
// grab all paths with movie content set
if (!m_pDS->query("select strPath,noUpdate from path"
" where (strContent = 'movies' or strContent = 'musicvideos')"
" and strPath NOT like 'multipath://%%'"
" order by strPath"))
return false;
while (!m_pDS->eof())
{
if (!m_pDS->fv("noUpdate").get_asBool())
paths.insert(m_pDS->fv("strPath").get_asString());
m_pDS->next();
}
m_pDS->close();
// then grab all tvshow paths
if (!m_pDS->query("select strPath,noUpdate from path"
" where ( strContent = 'tvshows'"
" or idPath in (select idPath from tvshowlinkpath))"
" and strPath NOT like 'multipath://%%'"
" order by strPath"))
return false;
while (!m_pDS->eof())
{
if (!m_pDS->fv("noUpdate").get_asBool())
paths.insert(m_pDS->fv("strPath").get_asString());
m_pDS->next();
}
m_pDS->close();
// finally grab all other paths holding a movie which is not a stack or a rar archive
// - this isnt perfect but it should do fine in most situations.
// reason we need it to hold a movie is stacks from different directories (cdx folders for instance)
// not making mistakes must take priority
if (!m_pDS->query("select strPath,noUpdate from path"
" where idPath in (select idPath from files join movie on movie.idFile=files.idFile)"
" and idPath NOT in (select idPath from tvshowlinkpath)"
" 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
" and idPath NOT in (select idPath from files where strFileName like 'index.bdmv')" // bluray folders get stacked to a single item in parent folder
" and strPath NOT like 'multipath://%%'"
" and strContent NOT in ('movies', 'tvshows', 'None')" // these have been added above
" order by strPath"))
return false;
while (!m_pDS->eof())
{
if (!m_pDS->fv("noUpdate").get_asBool())
paths.insert(m_pDS->fv("strPath").get_asString());
m_pDS->next();
}
m_pDS->close();
return true;
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
}
return false;
}
bool CVideoDatabase::GetPathsForTvShow(int idShow, set<int>& paths)
{
CStdString strSQL;
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
strSQL = PrepareSQL("SELECT DISTINCT idPath FROM files JOIN episode ON episode.idFile=files.idFile WHERE episode.idShow=%i",idShow);
m_pDS->query(strSQL.c_str());
while (!m_pDS->eof())
{
paths.insert(m_pDS->fv(0).get_asInt());
m_pDS->next();
}
m_pDS->close();
return true;
}
catch (...)
{
CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, strSQL.c_str());
}
return false;
}
int CVideoDatabase::RunQuery(const CStdString &sql)
{
unsigned int time = XbmcThreads::SystemClockMillis();
int rows = -1;
if (m_pDS->query(sql.c_str()))
{
rows = m_pDS->num_rows();
if (rows == 0)
m_pDS->close();
}
CLog::Log(LOGDEBUG, "%s took %d ms for %d items query: %s", __FUNCTION__, XbmcThreads::SystemClockMillis() - time, rows, sql.c_str());
return rows;
}
bool CVideoDatabase::GetSubPaths(const CStdString &basepath, vector< pair<int,string> >& subpaths)
{
CStdString sql;
try
{
if (!m_pDB.get() || !m_pDS.get())
return false;
CStdString path(basepath);
URIUtils::AddSlashAtEnd(path);
sql = PrepareSQL("SELECT idPath,strPath FROM path WHERE SUBSTR(strPath,1,%i)='%s'", StringUtils::utf8_strlen(path.c_str()), path.c_str());
m_pDS->query(sql.c_str());
while (!m_pDS->eof())
{
subpaths.push_back(make_pair(m_pDS->fv(0).get_asInt(), m_pDS->fv(1).get_asString()));
m_pDS->next();
}
m_pDS->close();
return true;
}
catch (...)
{
CLog::Log(LOGERROR, "%s error during query: %s",__FUNCTION__, sql.c_str());
}
return false;
}
int CVideoDatabase::AddPath(const CStdString& strPath, const CStdString &strDateAdded /*= "" */)
{
CStdString strSQL;
try
{
int idPath = GetPathId(strPath);
if (idPath >= 0)
return idPath; // already have the path
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
CStdString strPath1(strPath);
if (URIUtils::IsStack(strPath) || strPath.Mid(0,6).Equals("rar://") || strPath.Mid(0,6).Equals("zip://"))
URIUtils::GetParentPath(strPath,strPath1);
URIUtils::AddSlashAtEnd(strPath1);
// only set dateadded if we got one
if (!strDateAdded.empty())
strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper, dateAdded) values (NULL,'%s','','', '%s')", strPath1.c_str(), strDateAdded.c_str());
else
strSQL=PrepareSQL("insert into path (idPath, strPath, strContent, strScraper) values (NULL,'%s','','')", strPath1.c_str());
m_pDS->exec(strSQL.c_str());
idPath = (int)m_pDS->lastinsertid();
return idPath;
}
catch (...)
{
CLog::Log(LOGERROR, "%s unable to addpath (%s)", __FUNCTION__, strSQL.c_str());
}
return -1;
}
bool CVideoDatabase::GetPathHash(const CStdString &path, CStdString &hash)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
CStdString strSQL=PrepareSQL("select strHash from path where strPath='%s'", path.c_str());
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() == 0)
return false;
hash = m_pDS->fv("strHash").get_asString();
return true;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, path.c_str());
}
return false;
}
//********************************************************************************************************************************
int CVideoDatabase::AddFile(const CStdString& strFileNameAndPath)
{
CStdString strSQL = "";
try
{
int idFile;
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
CStdString strFileName, strPath;
SplitPath(strFileNameAndPath,strPath,strFileName);
int idPath = AddPath(strPath);
if (idPath < 0)
return -1;
CStdString strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() > 0)
{
idFile = m_pDS->fv("idFile").get_asInt() ;
m_pDS->close();
return idFile;
}
m_pDS->close();
strSQL=PrepareSQL("insert into files (idFile, idPath, strFileName) values(NULL, %i, '%s')", idPath, strFileName.c_str());
m_pDS->exec(strSQL.c_str());
idFile = (int)m_pDS->lastinsertid();
return idFile;
}
catch (...)
{
CLog::Log(LOGERROR, "%s unable to addfile (%s)", __FUNCTION__, strSQL.c_str());
}
return -1;
}
int CVideoDatabase::AddFile(const CFileItem& item)
{
if (item.IsVideoDb() && item.HasVideoInfoTag())
return AddFile(item.GetVideoInfoTag()->m_strFileNameAndPath);
return AddFile(item.GetPath());
}
void CVideoDatabase::UpdateFileDateAdded(int idFile, const CStdString& strFileNameAndPath)
{
if (idFile < 0 || strFileNameAndPath.empty())
return;
CStdString strSQL = "";
try
{
if (NULL == m_pDB.get()) return;
if (NULL == m_pDS.get()) return;
CStdString file = strFileNameAndPath;
if (URIUtils::IsStack(strFileNameAndPath))
file = CStackDirectory::GetFirstStackedFile(strFileNameAndPath);
if (URIUtils::IsInArchive(file))
file = CURL(file).GetHostName();
CDateTime dateAdded;
// Skip looking at the files ctime/mtime if defined by the user through as.xml
if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
{
// Let's try to get the modification datetime
struct __stat64 buffer;
if (CFile::Stat(file, &buffer) == 0)
{
time_t now = time(NULL);
time_t addedTime;
// Prefer the modification time if it's valid
if (g_advancedSettings.m_iVideoLibraryDateAdded == 1)
{
if ((time_t)buffer.st_mtime <= now)
addedTime = (time_t)buffer.st_mtime;
else
addedTime = (time_t)buffer.st_ctime;
}
// Use the newer of the creation and modification time
else
{
addedTime = max((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
// if the newer of the two dates is in the future, we try it with the older one
if (addedTime > now)
addedTime = min((time_t)buffer.st_ctime, (time_t)buffer.st_mtime);
}
// make sure the datetime does is not in the future
if (addedTime <= now)
{
struct tm *time = localtime(&addedTime);
if (time)
dateAdded = *time;
}
}
}
if (!dateAdded.IsValid())
dateAdded = CDateTime::GetCurrentDateTime();
strSQL = PrepareSQL("update files set dateAdded='%s' where idFile=%d", dateAdded.GetAsDBDateTime().c_str(), idFile);
m_pDS->exec(strSQL.c_str());
}
catch (...)
{
CLog::Log(LOGERROR, "%s unable to update dateadded for file (%s)", __FUNCTION__, strSQL.c_str());
}
}
bool CVideoDatabase::SetPathHash(const CStdString &path, const CStdString &hash)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
if (hash.IsEmpty())
{ // this is an empty folder - we need only add it to the path table
// if the path actually exists
if (!CDirectory::Exists(path))
return false;
}
int idPath = AddPath(path);
if (idPath < 0) return false;
CStdString strSQL=PrepareSQL("update path set strHash='%s' where idPath=%ld", hash.c_str(), idPath);
m_pDS->exec(strSQL.c_str());
return true;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s, %s) failed", __FUNCTION__, path.c_str(), hash.c_str());
}
return false;
}
bool CVideoDatabase::LinkMovieToTvshow(int idMovie, int idShow, bool bRemove)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
if (bRemove) // delete link
{
CStdString strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i and idShow=%i", idMovie, idShow);
m_pDS->exec(strSQL.c_str());
return true;
}
CStdString strSQL=PrepareSQL("insert into movielinktvshow (idShow,idMovie) values (%i,%i)", idShow,idMovie);
m_pDS->exec(strSQL.c_str());
return true;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%i, %i) failed", __FUNCTION__, idMovie, idShow);
}
return false;
}
bool CVideoDatabase::IsLinkedToTvshow(int idMovie)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
m_pDS->query(strSQL.c_str());
if (m_pDS->eof())
{
m_pDS->close();
return false;
}
m_pDS->close();
return true;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
}
return false;
}
bool CVideoDatabase::GetLinksToTvShow(int idMovie, vector<int>& ids)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
CStdString strSQL=PrepareSQL("select * from movielinktvshow where idMovie=%i", idMovie);
m_pDS2->query(strSQL.c_str());
while (!m_pDS2->eof())
{
ids.push_back(m_pDS2->fv(1).get_asInt());
m_pDS2->next();
}
m_pDS2->close();
return true;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idMovie);
}
return false;
}
//********************************************************************************************************************************
int CVideoDatabase::GetFileId(const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
CStdString strPath, strFileName;
SplitPath(strFilenameAndPath,strPath,strFileName);
int idPath = GetPathId(strPath);
if (idPath >= 0)
{
CStdString strSQL;
strSQL=PrepareSQL("select idFile from files where strFileName='%s' and idPath=%i", strFileName.c_str(),idPath);
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() > 0)
{
int idFile = m_pDS->fv("files.idFile").get_asInt();
m_pDS->close();
return idFile;
}
}
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
int CVideoDatabase::GetFileId(const CFileItem &item)
{
if (item.IsVideoDb() && item.HasVideoInfoTag())
return GetFileId(item.GetVideoInfoTag()->m_strFileNameAndPath);
return GetFileId(item.GetPath());
}
//********************************************************************************************************************************
int CVideoDatabase::GetMovieId(const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
int idMovie = -1;
// needed for query parameters
int idFile = GetFileId(strFilenameAndPath);
int idPath=-1;
CStdString strPath;
if (idFile < 0)
{
CStdString strFile;
SplitPath(strFilenameAndPath,strPath,strFile);
// have to join movieinfo table for correct results
idPath = GetPathId(strPath);
if (idPath < 0 && strPath != strFilenameAndPath)
return -1;
}
if (idFile == -1 && strPath != strFilenameAndPath)
return -1;
CStdString strSQL;
if (idFile == -1)
strSQL=PrepareSQL("select idMovie from movie join files on files.idFile=movie.idFile where files.idPath=%i",idPath);
else
strSQL=PrepareSQL("select idMovie from movie where idFile=%i", idFile);
CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() > 0)
idMovie = m_pDS->fv("idMovie").get_asInt();
m_pDS->close();
return idMovie;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
int CVideoDatabase::GetTvShowId(const CStdString& strPath)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
int idTvShow = -1;
// have to join movieinfo table for correct results
int idPath = GetPathId(strPath);
if (idPath < 0)
return -1;
CStdString strSQL;
CStdString strPath1=strPath;
CStdString strParent;
int iFound=0;
strSQL=PrepareSQL("select idShow from tvshowlinkpath where tvshowlinkpath.idPath=%i",idPath);
m_pDS->query(strSQL);
if (!m_pDS->eof())
iFound = 1;
while (iFound == 0 && URIUtils::GetParentPath(strPath1, strParent))
{
strSQL=PrepareSQL("select idShow from path,tvshowlinkpath where tvshowlinkpath.idPath=path.idPath and strPath='%s'",strParent.c_str());
m_pDS->query(strSQL.c_str());
if (!m_pDS->eof())
{
int idShow = m_pDS->fv("idShow").get_asInt();
if (idShow != -1)
iFound = 2;
}
strPath1 = strParent;
}
if (m_pDS->num_rows() > 0)
idTvShow = m_pDS->fv("idShow").get_asInt();
m_pDS->close();
return idTvShow;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
}
return -1;
}
int CVideoDatabase::GetEpisodeId(const CStdString& strFilenameAndPath, int idEpisode, int idSeason) // input value is episode/season number hint - for multiparters
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
// need this due to the nested GetEpisodeInfo query
auto_ptr<Dataset> pDS;
pDS.reset(m_pDB->CreateDataset());
if (NULL == pDS.get()) return -1;
int idFile = GetFileId(strFilenameAndPath);
if (idFile < 0)
return -1;
CStdString strSQL=PrepareSQL("select idEpisode from episode where idFile=%i", idFile);
CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
pDS->query(strSQL.c_str());
if (pDS->num_rows() > 0)
{
if (idEpisode == -1)
idEpisode = pDS->fv("episode.idEpisode").get_asInt();
else // use the hint!
{
while (!pDS->eof())
{
CVideoInfoTag tag;
int idTmpEpisode = pDS->fv("episode.idEpisode").get_asInt();
GetEpisodeInfo(strFilenameAndPath,tag,idTmpEpisode);
if (tag.m_iEpisode == idEpisode && (idSeason == -1 || tag.m_iSeason == idSeason)) {
// match on the episode hint, and there's no season hint or a season hint match
idEpisode = idTmpEpisode;
break;
}
pDS->next();
}
if (pDS->eof())
idEpisode = -1;
}
}
else
idEpisode = -1;
pDS->close();
return idEpisode;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
int CVideoDatabase::GetMusicVideoId(const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
int idFile = GetFileId(strFilenameAndPath);
if (idFile < 0)
return -1;
CStdString strSQL=PrepareSQL("select idMVideo from musicvideo where idFile=%i", idFile);
CLog::Log(LOGDEBUG, "%s (%s), query = %s", __FUNCTION__, strFilenameAndPath.c_str(), strSQL.c_str());
m_pDS->query(strSQL.c_str());
int idMVideo=-1;
if (m_pDS->num_rows() > 0)
idMVideo = m_pDS->fv("idMVideo").get_asInt();
m_pDS->close();
return idMVideo;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
//********************************************************************************************************************************
int CVideoDatabase::AddMovie(const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
int idMovie = GetMovieId(strFilenameAndPath);
if (idMovie < 0)
{
int idFile = AddFile(strFilenameAndPath);
if (idFile < 0)
return -1;
UpdateFileDateAdded(idFile, strFilenameAndPath);
CStdString strSQL=PrepareSQL("insert into movie (idMovie, idFile) values (NULL, %i)", idFile);
m_pDS->exec(strSQL.c_str());
idMovie = (int)m_pDS->lastinsertid();
// CommitTransaction();
}
return idMovie;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
int CVideoDatabase::AddTvShow(const CStdString& strPath)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
CStdString strSQL=PrepareSQL("select tvshowlinkpath.idShow from path,tvshowlinkpath where path.strPath='%s' and path.idPath=tvshowlinkpath.idPath",strPath.c_str());
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() != 0)
return m_pDS->fv("tvshowlinkpath.idShow").get_asInt();
strSQL=PrepareSQL("insert into tvshow (idShow) values (NULL)");
m_pDS->exec(strSQL.c_str());
int idTvShow = (int)m_pDS->lastinsertid();
// Get the creation datetime of the tvshow directory
CDateTime dateAdded;
// Skip looking at the files ctime/mtime if defined by the user through as.xml
if (g_advancedSettings.m_iVideoLibraryDateAdded > 0)
{
struct __stat64 buffer;
if (XFILE::CFile::Stat(strPath, &buffer) == 0)
{
time_t now = time(NULL);
// Make sure we have a valid date (i.e. not in the future)
if ((time_t)buffer.st_ctime <= now)
{
struct tm *time = localtime((const time_t*)&buffer.st_ctime);
if (time)
dateAdded = *time;
}
}
}
if (!dateAdded.IsValid())
dateAdded = CDateTime::GetCurrentDateTime();
int idPath = AddPath(strPath, dateAdded.GetAsDBDateTime());
strSQL=PrepareSQL("insert into tvshowlinkpath values (%i,%i)",idTvShow,idPath);
m_pDS->exec(strSQL.c_str());
// CommitTransaction();
return idTvShow;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
}
return -1;
}
//********************************************************************************************************************************
int CVideoDatabase::AddEpisode(int idShow, const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
int idFile = AddFile(strFilenameAndPath);
if (idFile < 0)
return -1;
UpdateFileDateAdded(idFile, strFilenameAndPath);
CStdString strSQL=PrepareSQL("insert into episode (idEpisode, idFile, idShow) values (NULL, %i, %i)", idFile, idShow);
m_pDS->exec(strSQL.c_str());
return (int)m_pDS->lastinsertid();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
int CVideoDatabase::AddMusicVideo(const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
int idMVideo = GetMusicVideoId(strFilenameAndPath);
if (idMVideo < 0)
{
int idFile = AddFile(strFilenameAndPath);
if (idFile < 0)
return -1;
UpdateFileDateAdded(idFile, strFilenameAndPath);
CStdString strSQL=PrepareSQL("insert into musicvideo (idMVideo, idFile) values (NULL, %i)", idFile);
m_pDS->exec(strSQL.c_str());
idMVideo = (int)m_pDS->lastinsertid();
}
return idMVideo;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
//********************************************************************************************************************************
int CVideoDatabase::AddToTable(const CStdString& table, const CStdString& firstField, const CStdString& secondField, const CStdString& value)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
CStdString strSQL = PrepareSQL("select %s from %s where %s like '%s'", firstField.c_str(), table.c_str(), secondField.c_str(), value.c_str());
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() == 0)
{
m_pDS->close();
// doesnt exists, add it
strSQL = PrepareSQL("insert into %s (%s, %s) values( NULL, '%s')", table.c_str(), firstField.c_str(), secondField.c_str(), value.c_str());
m_pDS->exec(strSQL.c_str());
int id = (int)m_pDS->lastinsertid();
return id;
}
else
{
int id = m_pDS->fv(firstField).get_asInt();
m_pDS->close();
return id;
}
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, value.c_str() );
}
return -1;
}
int CVideoDatabase::AddSet(const CStdString& strSet)
{
return AddToTable("sets", "idSet", "strSet", strSet);
}
int CVideoDatabase::AddGenre(const CStdString& strGenre)
{
return AddToTable("genre", "idGenre", "strGenre", strGenre);
}
int CVideoDatabase::AddStudio(const CStdString& strStudio)
{
return AddToTable("studio", "idStudio", "strStudio", strStudio);
}
//********************************************************************************************************************************
int CVideoDatabase::AddCountry(const CStdString& strCountry)
{
return AddToTable("country", "idCountry", "strCountry", strCountry);
}
int CVideoDatabase::AddActor(const CStdString& strActor, const CStdString& thumbURLs, const CStdString &thumb)
{
try
{
if (NULL == m_pDB.get()) return -1;
if (NULL == m_pDS.get()) return -1;
int idActor = -1;
CStdString strSQL=PrepareSQL("select idActor from actors where strActor like '%s'", strActor.c_str());
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() == 0)
{
m_pDS->close();
// doesnt exists, add it
strSQL=PrepareSQL("insert into actors (idActor, strActor, strThumb) values( NULL, '%s','%s')", strActor.c_str(),thumbURLs.c_str());
m_pDS->exec(strSQL.c_str());
idActor = (int)m_pDS->lastinsertid();
}
else
{
idActor = m_pDS->fv("idActor").get_asInt();
m_pDS->close();
// update the thumb url's
if (!thumbURLs.IsEmpty())
{
strSQL=PrepareSQL("update actors set strThumb='%s' where idActor=%i",thumbURLs.c_str(),idActor);
m_pDS->exec(strSQL.c_str());
}
}
// add artwork
if (!thumb.IsEmpty())
SetArtForItem(idActor, "actor", "thumb", thumb);
return idActor;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strActor.c_str() );
}
return -1;
}
void CVideoDatabase::AddLinkToActor(const char *table, int actorID, const char *secondField, int secondID, const CStdString &role, int order)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
CStdString strSQL=PrepareSQL("select * from %s where idActor=%i and %s=%i", table, actorID, secondField, secondID);
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() == 0)
{
// doesnt exists, add it
strSQL=PrepareSQL("insert into %s (idActor, %s, strRole, iOrder) values(%i,%i,'%s',%i)", table, secondField, actorID, secondID, role.c_str(), order);
m_pDS->exec(strSQL.c_str());
}
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
}
}
void CVideoDatabase::AddToLinkTable(const char *table, const char *firstField, int firstID, const char *secondField, int secondID)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
CStdString strSQL=PrepareSQL("select * from %s where %s=%i and %s=%i", table, firstField, firstID, secondField, secondID);
m_pDS->query(strSQL.c_str());
if (m_pDS->num_rows() == 0)
{
// doesnt exists, add it
strSQL=PrepareSQL("insert into %s (%s,%s) values(%i,%i)", table, firstField, secondField, firstID, secondID);
m_pDS->exec(strSQL.c_str());
}
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
}
}
//****Sets****
void CVideoDatabase::AddSetToMovie(int idMovie, int idSet)
{
AddToLinkTable("setlinkmovie", "idSet", idSet, "idMovie", idMovie);
}
//****Actors****
void CVideoDatabase::AddActorToMovie(int idMovie, int idActor, const CStdString& strRole, int order)
{
AddLinkToActor("actorlinkmovie", idActor, "idMovie", idMovie, strRole, order);
}
void CVideoDatabase::AddActorToTvShow(int idTvShow, int idActor, const CStdString& strRole, int order)
{
AddLinkToActor("actorlinktvshow", idActor, "idShow", idTvShow, strRole, order);
}
void CVideoDatabase::AddActorToEpisode(int idEpisode, int idActor, const CStdString& strRole, int order)
{
AddLinkToActor("actorlinkepisode", idActor, "idEpisode", idEpisode, strRole, order);
}
void CVideoDatabase::AddArtistToMusicVideo(int idMVideo, int idArtist)
{
AddToLinkTable("artistlinkmusicvideo", "idArtist", idArtist, "idMVideo", idMVideo);
}
//****Directors + Writers****
void CVideoDatabase::AddDirectorToMovie(int idMovie, int idDirector)
{
AddToLinkTable("directorlinkmovie", "idDirector", idDirector, "idMovie", idMovie);
}
void CVideoDatabase::AddDirectorToTvShow(int idTvShow, int idDirector)
{
AddToLinkTable("directorlinktvshow", "idDirector", idDirector, "idShow", idTvShow);
}
void CVideoDatabase::AddWriterToEpisode(int idEpisode, int idWriter)
{
AddToLinkTable("writerlinkepisode", "idWriter", idWriter, "idEpisode", idEpisode);
}
void CVideoDatabase::AddWriterToMovie(int idMovie, int idWriter)
{
AddToLinkTable("writerlinkmovie", "idWriter", idWriter, "idMovie", idMovie);
}
void CVideoDatabase::AddDirectorToEpisode(int idEpisode, int idDirector)
{
AddToLinkTable("directorlinkepisode", "idDirector", idDirector, "idEpisode", idEpisode);
}
void CVideoDatabase::AddDirectorToMusicVideo(int idMVideo, int idDirector)
{
AddToLinkTable("directorlinkmusicvideo", "idDirector", idDirector, "idMVideo", idMVideo);
}
//****Studios****
void CVideoDatabase::AddStudioToMovie(int idMovie, int idStudio)
{
AddToLinkTable("studiolinkmovie", "idStudio", idStudio, "idMovie", idMovie);
}
void CVideoDatabase::AddStudioToTvShow(int idTvShow, int idStudio)
{
AddToLinkTable("studiolinktvshow", "idStudio", idStudio, "idShow", idTvShow);
}
void CVideoDatabase::AddStudioToMusicVideo(int idMVideo, int idStudio)
{
AddToLinkTable("studiolinkmusicvideo", "idStudio", idStudio, "idMVideo", idMVideo);
}
//****Genres****
void CVideoDatabase::AddGenreToMovie(int idMovie, int idGenre)
{
AddToLinkTable("genrelinkmovie", "idGenre", idGenre, "idMovie", idMovie);
}
void CVideoDatabase::AddGenreToTvShow(int idTvShow, int idGenre)
{
AddToLinkTable("genrelinktvshow", "idGenre", idGenre, "idShow", idTvShow);
}
void CVideoDatabase::AddGenreToMusicVideo(int idMVideo, int idGenre)
{
AddToLinkTable("genrelinkmusicvideo", "idGenre", idGenre, "idMVideo", idMVideo);
}
//****Country****
void CVideoDatabase::AddCountryToMovie(int idMovie, int idCountry)
{
AddToLinkTable("countrylinkmovie", "idCountry", idCountry, "idMovie", idMovie);
}
//********************************************************************************************************************************
bool CVideoDatabase::LoadVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details)
{
if (GetMovieInfo(strFilenameAndPath, details))
{
CLog::Log(LOGDEBUG,"%s, got movie info!", __FUNCTION__);
CLog::Log(LOGDEBUG," Title = %s", details.m_strTitle.c_str());
}
else if (GetEpisodeInfo(strFilenameAndPath, details))
{
CLog::Log(LOGDEBUG,"%s, got episode info!", __FUNCTION__);
CLog::Log(LOGDEBUG," Title = %s", details.m_strTitle.c_str());
}
else if (GetMusicVideoInfo(strFilenameAndPath, details))
{
CLog::Log(LOGDEBUG,"%s, got music video info!", __FUNCTION__);
CLog::Log(LOGDEBUG," Title = %s", details.m_strTitle.c_str());
}
return !details.IsEmpty();
}
bool CVideoDatabase::HasMovieInfo(const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
int idMovie = GetMovieId(strFilenameAndPath);
return (idMovie > 0); // index of zero is also invalid
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return false;
}
bool CVideoDatabase::HasTvShowInfo(const CStdString& strPath)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
int idTvShow = GetTvShowId(strPath);
return (idTvShow > 0); // index of zero is also invalid
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
}
return false;
}
bool CVideoDatabase::HasEpisodeInfo(const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
int idEpisode = GetEpisodeId(strFilenameAndPath);
return (idEpisode > 0); // index of zero is also invalid
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return false;
}
bool CVideoDatabase::HasMusicVideoInfo(const CStdString& strFilenameAndPath)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
int idMVideo = GetMusicVideoId(strFilenameAndPath);
return (idMVideo > 0); // index of zero is also invalid
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return false;
}
void CVideoDatabase::DeleteDetailsForTvShow(const CStdString& strPath, int idTvShow /* = -1 */)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
if (idTvShow < 0)
{
idTvShow = GetTvShowId(strPath);
if (idTvShow < 0)
return;
}
CStdString strSQL;
strSQL=PrepareSQL("delete from genrelinktvshow where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from actorlinktvshow where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from directorlinktvshow where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from studiolinktvshow where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
// remove all info other than the id
// we do this due to the way we have the link between the file + movie tables.
strSQL = "update tvshow set ";
for (int iType = VIDEODB_ID_TV_MIN + 1; iType < VIDEODB_ID_TV_MAX; iType++)
{
CStdString column;
column.Format("c%02d=NULL,", iType);
strSQL += column;
}
strSQL = strSQL.Mid(0, strSQL.size() - 1) + PrepareSQL(" where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
}
}
//********************************************************************************************************************************
void CVideoDatabase::GetMoviesByActor(const CStdString& strActor, CFileItemList& items)
{
Filter filter;
filter.join = "LEFT JOIN actorlinkmovie ON actorlinkmovie.idMovie=movieview.idMovie "
"LEFT JOIN actors a ON a.idActor=actorlinkmovie.idActor "
"LEFT JOIN directorlinkmovie ON directorlinkmovie.idMovie=movieview.idMovie "
"LEFT JOIN actors d ON d.idActor=directorlinkmovie.idDirector";
filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
filter.group = "movieview.idMovie";
GetMoviesByWhere("videodb://1/2/", filter, items);
}
void CVideoDatabase::GetTvShowsByActor(const CStdString& strActor, CFileItemList& items)
{
Filter filter;
filter.join = "LEFT JOIN actorlinktvshow ON actorlinktvshow.idShow=tvshowview.idShow "
"LEFT JOIN actors a ON a.idActor=actorlinktvshow.idActor "
"LEFT JOIN directorlinktvshow ON directorlinktvshow.idShow=tvshowview.idShow "
"LEFT JOIN actors d ON d.idActor=directorlinktvshow.idDirector";
filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
filter.group = "tvshowview.idShow";
GetTvShowsByWhere("videodb://2/2/", filter, items);
}
void CVideoDatabase::GetEpisodesByActor(const CStdString& strActor, CFileItemList& items)
{
Filter filter;
filter.join = "LEFT JOIN actorlinkepisode ON actorlinkepisode.idEpisode=episodeview.idEpisode "
"LEFT JOIN actors a ON a.idActor=actorlinkepisode.idActor "
"LEFT JOIN directorlinkepisode ON directorlinkepisode.idEpisode=episodeview.idEpisode "
"LEFT JOIN actors d ON d.idActor=directorlinkepisode.idDirector";
filter.where = PrepareSQL("a.strActor='%s' OR d.strActor='%s'", strActor.c_str(), strActor.c_str());
filter.group = "episodeview.idEpisode";
GetEpisodesByWhere("videodb://2/2/", filter, items);
}
void CVideoDatabase::GetMusicVideosByArtist(const CStdString& strArtist, CFileItemList& items)
{
try
{
items.Clear();
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
CStdString strSQL;
if (strArtist.IsEmpty()) // TODO: SMARTPLAYLISTS what is this here for???
strSQL=PrepareSQL("select distinct * from musicvideoview join artistlinkmusicvideo on artistlinkmusicvideo.idMVideo=musicvideoview.idMVideo join actors on actors.idActor=artistlinkmusicvideo.idArtist");
else
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());
m_pDS->query( strSQL.c_str() );
while (!m_pDS->eof())
{
CVideoInfoTag tag = GetDetailsForMusicVideo(m_pDS);
CFileItemPtr pItem(new CFileItem(tag));
pItem->SetLabel(StringUtils::Join(tag.m_artist, g_advancedSettings.m_videoItemSeparator));
items.Add(pItem);
m_pDS->next();
}
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strArtist.c_str());
}
}
//********************************************************************************************************************************
bool CVideoDatabase::GetMovieInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMovie /* = -1 */)
{
try
{
// TODO: Optimize this - no need for all the queries!
if (idMovie < 0)
idMovie = GetMovieId(strFilenameAndPath);
if (idMovie < 0) return false;
CStdString sql = PrepareSQL("select * from movieview where idMovie=%i", idMovie);
if (!m_pDS->query(sql.c_str()))
return false;
details = GetDetailsForMovie(m_pDS, true);
return !details.IsEmpty();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return false;
}
//********************************************************************************************************************************
bool CVideoDatabase::GetTvShowInfo(const CStdString& strPath, CVideoInfoTag& details, int idTvShow /* = -1 */)
{
try
{
if (idTvShow < 0)
idTvShow = GetTvShowId(strPath);
if (idTvShow < 0) return false;
CStdString sql = PrepareSQL("SELECT * FROM tvshowview WHERE idShow=%i", idTvShow);
if (!m_pDS->query(sql.c_str()))
return false;
details = GetDetailsForTvShow(m_pDS, true);
return !details.IsEmpty();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
}
return false;
}
bool CVideoDatabase::GetEpisodeInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idEpisode /* = -1 */)
{
try
{
// TODO: Optimize this - no need for all the queries!
if (idEpisode < 0)
idEpisode = GetEpisodeId(strFilenameAndPath);
if (idEpisode < 0) return false;
CStdString sql = PrepareSQL("select * from episodeview where idEpisode=%i",idEpisode);
if (!m_pDS->query(sql.c_str()))
return false;
details = GetDetailsForEpisode(m_pDS, true);
return !details.IsEmpty();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return false;
}
bool CVideoDatabase::GetMusicVideoInfo(const CStdString& strFilenameAndPath, CVideoInfoTag& details, int idMVideo /* = -1 */)
{
try
{
// TODO: Optimize this - no need for all the queries!
if (idMVideo < 0)
idMVideo = GetMusicVideoId(strFilenameAndPath);
if (idMVideo < 0) return false;
CStdString sql = PrepareSQL("select * from musicvideoview where idMVideo=%i", idMVideo);
if (!m_pDS->query(sql.c_str()))
return false;
details = GetDetailsForMusicVideo(m_pDS);
return !details.IsEmpty();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return false;
}
bool CVideoDatabase::GetSetInfo(int idSet, CVideoInfoTag& details)
{
try
{
if (idSet < 0)
return false;
Filter filter;
filter.where = PrepareSQL("sets.idSet=%d", idSet);
CFileItemList items;
if (!GetSetsByWhere("videodb://1/7/", filter, items) ||
items.Size() != 1 ||
!items[0]->HasVideoInfoTag())
return false;
details = *(items[0]->GetVideoInfoTag());
return !details.IsEmpty();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%d) failed", __FUNCTION__, idSet);
}
return false;
}
void CVideoDatabase::AddGenreAndDirectorsAndStudios(const CVideoInfoTag& details, vector<int>& vecDirectors, vector<int>& vecGenres, vector<int>& vecStudios)
{
// add all directors
for (unsigned int i = 0; i < details.m_director.size(); i++)
vecDirectors.push_back(AddActor(details.m_director[i],""));
// add all genres
for (unsigned int i = 0; i < details.m_genre.size(); i++)
vecGenres.push_back(AddGenre(details.m_genre[i]));
// add all studios
for (unsigned int i = 0; i < details.m_studio.size(); i++)
vecStudios.push_back(AddStudio(details.m_studio[i]));
}
CStdString CVideoDatabase::GetValueString(const CVideoInfoTag &details, int min, int max, const SDbTableOffsets *offsets) const
{
CStdString sql;
for (int i = min + 1; i < max; ++i)
{
switch (offsets[i].type)
{
case VIDEODB_TYPE_STRING:
sql += PrepareSQL("c%02d='%s',", i, ((CStdString*)(((char*)&details)+offsets[i].offset))->c_str());
break;
case VIDEODB_TYPE_INT:
sql += PrepareSQL("c%02d='%i',", i, *(int*)(((char*)&details)+offsets[i].offset));
break;
case VIDEODB_TYPE_COUNT:
{
int value = *(int*)(((char*)&details)+offsets[i].offset);
if (value)
sql += PrepareSQL("c%02d=%i,", i, value);
else
sql += PrepareSQL("c%02d=NULL,", i);
}
break;
case VIDEODB_TYPE_BOOL:
sql += PrepareSQL("c%02d='%s',", i, *(bool*)(((char*)&details)+offsets[i].offset)?"true":"false");
break;
case VIDEODB_TYPE_FLOAT:
sql += PrepareSQL("c%02d='%f',", i, *(float*)(((char*)&details)+offsets[i].offset));
break;
case VIDEODB_TYPE_STRINGARRAY:
sql += PrepareSQL("c%02d='%s',", i, StringUtils::Join(*((std::vector<std::string>*)(((char*)&details)+offsets[i].offset)), g_advancedSettings.m_videoItemSeparator).c_str());
break;
case VIDEODB_TYPE_DATE:
sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDate().c_str());
break;
case VIDEODB_TYPE_DATETIME:
sql += PrepareSQL("c%02d='%s',", i, ((CDateTime*)(((char*)&details)+offsets[i].offset))->GetAsDBDateTime().c_str());
break;
}
}
sql.TrimRight(',');
return sql;
}
//********************************************************************************************************************************
int CVideoDatabase::SetDetailsForMovie(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMovie /* = -1 */)
{
try
{
BeginTransaction();
if (idMovie < 0)
idMovie = GetMovieId(strFilenameAndPath);
if (idMovie > -1)
DeleteMovie(strFilenameAndPath, true, idMovie); // true to keep the table entry
else
{
// only add a new movie if we don't already have a valid idMovie
// (DeleteMovie is called with bKeepId == true so the movie won't
// be removed from the movie table)
idMovie = AddMovie(strFilenameAndPath);
if (idMovie < 0)
{
CommitTransaction();
return idMovie;
}
}
vector<int> vecDirectors;
vector<int> vecGenres;
vector<int> vecStudios;
AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
for (unsigned int i = 0; i < vecGenres.size(); ++i)
AddGenreToMovie(idMovie, vecGenres[i]);
for (unsigned int i = 0; i < vecDirectors.size(); ++i)
AddDirectorToMovie(idMovie, vecDirectors[i]);
for (unsigned int i = 0; i < vecStudios.size(); ++i)
AddStudioToMovie(idMovie, vecStudios[i]);
// add writers...
for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
AddWriterToMovie(idMovie, AddActor(details.m_writingCredits[i],""));
// add cast...
int order = 0;
for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
{
int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
AddActorToMovie(idMovie, idActor, it->strRole, order++);
}
// add sets...
for (unsigned int i = 0; i < details.m_set.size(); i++)
{
int idSet = AddSet(details.m_set[i]);
// add art if not available
map<string, string> setArt;
if (!GetArtForItem(idSet, "set", setArt))
SetArtForItem(idSet, "set", artwork);
AddSetToMovie(idMovie, idSet);
}
// add countries...
for (unsigned int i = 0; i < details.m_country.size(); i++)
AddCountryToMovie(idMovie, AddCountry(details.m_country[i]));
if (details.HasStreamDetails())
SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
SetArtForItem(idMovie, "movie", artwork);
// update our movie table (we know it was added already above)
// and insert the new row
CStdString sql = "update movie set " + GetValueString(details, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets);
sql += PrepareSQL(" where idMovie=%i", idMovie);
m_pDS->exec(sql.c_str());
CommitTransaction();
return idMovie;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
int CVideoDatabase::SetDetailsForTvShow(const CStdString& strPath, const CVideoInfoTag& details, const map<string, string> &artwork, const map<int, string> &seasonArt, int idTvShow /*= -1 */)
{
try
{
if (!m_pDB.get() || !m_pDS.get())
{
CLog::Log(LOGERROR, "%s: called without database open", __FUNCTION__);
return -1;
}
BeginTransaction();
if (idTvShow < 0)
idTvShow = GetTvShowId(strPath);
if (idTvShow > -1)
DeleteDetailsForTvShow(strPath, idTvShow);
else
{
idTvShow = AddTvShow(strPath);
if (idTvShow < 0)
{
CommitTransaction();
return idTvShow;
}
}
vector<int> vecDirectors;
vector<int> vecGenres;
vector<int> vecStudios;
AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
// add cast...
int order = 0;
for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
{
int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
AddActorToTvShow(idTvShow, idActor, it->strRole, order++);
}
unsigned int i;
for (i = 0; i < vecGenres.size(); ++i)
AddGenreToTvShow(idTvShow, vecGenres[i]);
for (i = 0; i < vecDirectors.size(); ++i)
AddDirectorToTvShow(idTvShow, vecDirectors[i]);
for (i = 0; i < vecStudios.size(); ++i)
AddStudioToTvShow(idTvShow, vecStudios[i]);
// add "all seasons" - the rest are added in SetDetailsForEpisode
AddSeason(idTvShow, -1);
SetArtForItem(idTvShow, "tvshow", artwork);
for (map<int, string>::const_iterator i = seasonArt.begin(); i != seasonArt.end(); ++i)
{
int idSeason = AddSeason(idTvShow, i->first);
if (idSeason > -1)
SetArtForItem(idSeason, "season", "thumb", i->second);
}
// and insert the new row
CStdString sql = "update tvshow set " + GetValueString(details, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets);
sql += PrepareSQL("where idShow=%i", idTvShow);
m_pDS->exec(sql.c_str());
CommitTransaction();
return idTvShow;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
}
return -1;
}
int CVideoDatabase::SetDetailsForEpisode(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idShow, int idEpisode)
{
try
{
BeginTransaction();
if (idEpisode < 0)
idEpisode = GetEpisodeId(strFilenameAndPath);
if (idEpisode > 0)
DeleteEpisode(strFilenameAndPath, idEpisode, true); // true to keep the table entry
else
{
// only add a new episode if we don't already have a valid idEpisode
// (DeleteEpisode is called with bKeepId == true so the episode won't
// be removed from the episode table)
idEpisode = AddEpisode(idShow,strFilenameAndPath);
if (idEpisode < 0)
{
CommitTransaction();
return -1;
}
}
vector<int> vecDirectors;
vector<int> vecGenres;
vector<int> vecStudios;
AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
// add cast...
int order = 0;
for (CVideoInfoTag::iCast it = details.m_cast.begin(); it != details.m_cast.end(); ++it)
{
int idActor = AddActor(it->strName, it->thumbUrl.m_xml, it->thumb);
AddActorToEpisode(idEpisode, idActor, it->strRole, order++);
}
// add writers...
for (unsigned int i = 0; i < details.m_writingCredits.size(); i++)
AddWriterToEpisode(idEpisode, AddActor(details.m_writingCredits[i],""));
for (unsigned int i = 0; i < vecDirectors.size(); ++i)
{
AddDirectorToEpisode(idEpisode, vecDirectors[i]);
}
if (details.HasStreamDetails())
{
if (details.m_iFileId != -1)
SetStreamDetailsForFileId(details.m_streamDetails, details.m_iFileId);
else
SetStreamDetailsForFile(details.m_streamDetails, strFilenameAndPath);
}
// ensure we have this season already added
AddSeason(idShow, details.m_iSeason);
SetArtForItem(idEpisode, "episode", artwork);
// and insert the new row
CStdString sql = "update episode set " + GetValueString(details, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets);
sql += PrepareSQL("where idEpisode=%i", idEpisode);
m_pDS->exec(sql.c_str());
CommitTransaction();
return idEpisode;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
int CVideoDatabase::GetSeasonId(int showID, int season)
{
CStdString sql = PrepareSQL("idShow=%i AND season=%i", showID, season);
CStdString id = GetSingleValue("seasons", "idSeason", sql);
if (id.IsEmpty())
return -1;
return strtol(id.c_str(), NULL, 10);
}
int CVideoDatabase::AddSeason(int showID, int season)
{
int seasonId = GetSeasonId(showID, season);
if (seasonId < 0)
{
if (ExecuteQuery(PrepareSQL("INSERT INTO seasons (idShow,season) VALUES(%i,%i)", showID, season)))
seasonId = (int)m_pDS->lastinsertid();
}
return seasonId;
}
int CVideoDatabase::SetDetailsForMusicVideo(const CStdString& strFilenameAndPath, const CVideoInfoTag& details, const map<string, string> &artwork, int idMVideo /* = -1 */)
{
try
{
BeginTransaction();
if (idMVideo < 0)
idMVideo = GetMusicVideoId(strFilenameAndPath);
if (idMVideo > -1)
DeleteMusicVideo(strFilenameAndPath, true, idMVideo); // Keep id
else
{
// only add a new musicvideo if we don't already have a valid idMVideo
// (DeleteMusicVideo is called with bKeepId == true so the musicvideo won't
// be removed from the musicvideo table)
idMVideo = AddMusicVideo(strFilenameAndPath);
if (idMVideo < 0)
{
CommitTransaction();
return -1;
}
}
vector<int> vecDirectors;
vector<int> vecGenres;
vector<int> vecStudios;
AddGenreAndDirectorsAndStudios(details,vecDirectors,vecGenres,vecStudios);
// add artists...
if (!details.m_artist.empty())
{
for (unsigned int i = 0; i < details.m_artist.size(); i++)
{
CStdString artist = details.m_artist[i];
artist.Trim();
int idArtist = AddActor(artist,"");
AddArtistToMusicVideo(idMVideo, idArtist);
}
}
unsigned int i;
for (i = 0; i < vecGenres.size(); ++i)
{
AddGenreToMusicVideo(idMVideo, vecGenres[i]);
}
for (i = 0; i < vecDirectors.size(); ++i)
{
AddDirectorToMusicVideo(idMVideo, vecDirectors[i]);
}
for (i = 0; i < vecStudios.size(); ++i)
{
AddStudioToMusicVideo(idMVideo, vecStudios[i]);
}
if (details.HasStreamDetails())
SetStreamDetailsForFileId(details.m_streamDetails, GetFileId(strFilenameAndPath));
SetArtForItem(idMVideo, "musicvideo", artwork);
// update our movie table (we know it was added already above)
// and insert the new row
CStdString sql = "update musicvideo set " + GetValueString(details, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets);
sql += PrepareSQL(" where idMVideo=%i", idMVideo);
m_pDS->exec(sql.c_str());
CommitTransaction();
return idMVideo;
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
return -1;
}
void CVideoDatabase::SetStreamDetailsForFile(const CStreamDetails& details, const CStdString &strFileNameAndPath)
{
// AddFile checks to make sure the file isn't already in the DB first
int idFile = AddFile(strFileNameAndPath);
if (idFile < 0)
return;
SetStreamDetailsForFileId(details, idFile);
}
void CVideoDatabase::SetStreamDetailsForFileId(const CStreamDetails& details, int idFile)
{
if (idFile < 0)
return;
try
{
BeginTransaction();
m_pDS->exec(PrepareSQL("DELETE FROM streamdetails WHERE idFile = %i", idFile));
for (int i=1; i<=details.GetVideoStreamCount(); i++)
{
m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
"(idFile, iStreamType, strVideoCodec, fVideoAspect, iVideoWidth, iVideoHeight, iVideoDuration) "
"VALUES (%i,%i,'%s',%f,%i,%i,%i)",
idFile, (int)CStreamDetail::VIDEO,
details.GetVideoCodec(i).c_str(), details.GetVideoAspect(i),
details.GetVideoWidth(i), details.GetVideoHeight(i), details.GetVideoDuration(i)));
}
for (int i=1; i<=details.GetAudioStreamCount(); i++)
{
m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
"(idFile, iStreamType, strAudioCodec, iAudioChannels, strAudioLanguage) "
"VALUES (%i,%i,'%s',%i,'%s')",
idFile, (int)CStreamDetail::AUDIO,
details.GetAudioCodec(i).c_str(), details.GetAudioChannels(i),
details.GetAudioLanguage(i).c_str()));
}
for (int i=1; i<=details.GetSubtitleStreamCount(); i++)
{
m_pDS->exec(PrepareSQL("INSERT INTO streamdetails "
"(idFile, iStreamType, strSubtitleLanguage) "
"VALUES (%i,%i,'%s')",
idFile, (int)CStreamDetail::SUBTITLE,
details.GetSubtitleLanguage(i).c_str()));
}
CommitTransaction();
}
catch (...)
{
RollbackTransaction();
CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idFile);
}
}
//********************************************************************************************************************************
void CVideoDatabase::GetFilePathById(int idMovie, CStdString &filePath, VIDEODB_CONTENT_TYPE iType)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
if (idMovie < 0) return ;
CStdString strSQL;
if (iType == VIDEODB_CONTENT_MOVIES)
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 );
if (iType == VIDEODB_CONTENT_EPISODES)
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 );
if (iType == VIDEODB_CONTENT_TVSHOWS)
strSQL=PrepareSQL("select path.strPath from path,tvshowlinkpath where path.idPath=tvshowlinkpath.idPath and tvshowlinkpath.idShow=%i", idMovie );
if (iType ==VIDEODB_CONTENT_MUSICVIDEOS)
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 );
m_pDS->query( strSQL.c_str() );
if (!m_pDS->eof())
{
if (iType != VIDEODB_CONTENT_TVSHOWS)
{
CStdString fileName = m_pDS->fv("files.strFilename").get_asString();
ConstructPath(filePath,m_pDS->fv("path.strPath").get_asString(),fileName);
}
else
filePath = m_pDS->fv("path.strPath").get_asString();
}
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
}
}
//********************************************************************************************************************************
void CVideoDatabase::GetBookMarksForFile(const CStdString& strFilenameAndPath, VECBOOKMARKS& bookmarks, CBookmark::EType type /*= CBookmark::STANDARD*/, bool bAppend)
{
try
{
int idFile = GetFileId(strFilenameAndPath);
if (idFile < 0) return ;
if (!bAppend)
bookmarks.erase(bookmarks.begin(), bookmarks.end());
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
CStdString strSQL=PrepareSQL("select * from bookmark where idFile=%i and type=%i order by timeInSeconds", idFile, (int)type);
m_pDS->query( strSQL.c_str() );
while (!m_pDS->eof())
{
CBookmark bookmark;
bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
bookmark.playerState = m_pDS->fv("playerState").get_asString();
bookmark.player = m_pDS->fv("player").get_asString();
bookmark.type = type;
if (type == CBookmark::EPISODE)
{
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);
m_pDS2->query(strSQL2.c_str());
bookmark.episodeNumber = m_pDS2->fv(0).get_asInt();
bookmark.seasonNumber = m_pDS2->fv(1).get_asInt();
m_pDS2->close();
}
bookmarks.push_back(bookmark);
m_pDS->next();
}
//sort(bookmarks.begin(), bookmarks.end(), SortBookmarks);
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
}
bool CVideoDatabase::GetResumeBookMark(const CStdString& strFilenameAndPath, CBookmark &bookmark)
{
VECBOOKMARKS bookmarks;
GetBookMarksForFile(strFilenameAndPath, bookmarks, CBookmark::RESUME);
if (bookmarks.size() > 0)
{
bookmark = bookmarks[0];
return true;
}
return false;
}
void CVideoDatabase::DeleteResumeBookMark(const CStdString &strFilenameAndPath)
{
if (!m_pDB.get() || !m_pDS.get())
return;
int fileID = GetFileId(strFilenameAndPath);
if (fileID < -1)
return;
try
{
CStdString sql = PrepareSQL("delete from bookmark where idFile=%i and type=%i", fileID, CBookmark::RESUME);
m_pDS->exec(sql.c_str());
}
catch(...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
}
void CVideoDatabase::GetEpisodesByFile(const CStdString& strFilenameAndPath, vector<CVideoInfoTag>& episodes)
{
try
{
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);
m_pDS->query(strSQL.c_str());
while (!m_pDS->eof())
{
episodes.push_back(GetDetailsForEpisode(m_pDS));
m_pDS->next();
}
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
}
//********************************************************************************************************************************
void CVideoDatabase::AddBookMarkToFile(const CStdString& strFilenameAndPath, const CBookmark &bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
{
try
{
int idFile = AddFile(strFilenameAndPath);
if (idFile < 0)
return;
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
CStdString strSQL;
int idBookmark=-1;
if (type == CBookmark::RESUME) // get the same resume mark bookmark each time type
{
strSQL=PrepareSQL("select idBookmark from bookmark where idFile=%i and type=1", idFile);
}
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
{
/* get a bookmark within the same time as previous */
double mintime = bookmark.timeInSeconds - 0.5f;
double maxtime = bookmark.timeInSeconds + 0.5f;
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());
}
if (type != CBookmark::EPISODE)
{
// get current id
m_pDS->query( strSQL.c_str() );
if (m_pDS->num_rows() != 0)
idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
m_pDS->close();
}
// update or insert depending if it existed before
if (idBookmark >= 0 )
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);
else
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);
m_pDS->exec(strSQL.c_str());
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
}
void CVideoDatabase::ClearBookMarkOfFile(const CStdString& strFilenameAndPath, CBookmark& bookmark, CBookmark::EType type /*= CBookmark::STANDARD*/)
{
try
{
int idFile = GetFileId(strFilenameAndPath);
if (idFile < 0) return ;
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
/* a litle bit uggly, we clear first bookmark that is within one second of given */
/* should be no problem since we never add bookmarks that are closer than that */
double mintime = bookmark.timeInSeconds - 0.5f;
double maxtime = bookmark.timeInSeconds + 0.5f;
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);
m_pDS->query( strSQL.c_str() );
if (m_pDS->num_rows() != 0)
{
int idBookmark = m_pDS->get_field_value("idBookmark").get_asInt();
strSQL=PrepareSQL("delete from bookmark where idBookmark=%i",idBookmark);
m_pDS->exec(strSQL.c_str());
if (type == CBookmark::EPISODE)
{
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);
m_pDS->exec(strSQL.c_str());
}
}
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
}
//********************************************************************************************************************************
void CVideoDatabase::ClearBookMarksOfFile(const CStdString& strFilenameAndPath, CBookmark::EType type /*= CBookmark::STANDARD*/)
{
try
{
int idFile = GetFileId(strFilenameAndPath);
if (idFile < 0) return ;
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
CStdString strSQL=PrepareSQL("delete from bookmark where idFile=%i and type=%i", idFile, (int)type);
m_pDS->exec(strSQL.c_str());
if (type == CBookmark::EPISODE)
{
strSQL=PrepareSQL("update episode set c%02d=-1 where idFile=%i", VIDEODB_ID_EPISODE_BOOKMARK, idFile);
m_pDS->exec(strSQL.c_str());
}
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
}
bool CVideoDatabase::GetBookMarkForEpisode(const CVideoInfoTag& tag, CBookmark& bookmark)
{
try
{
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);
m_pDS->query( strSQL.c_str() );
if (!m_pDS->eof())
{
bookmark.timeInSeconds = m_pDS->fv("timeInSeconds").get_asDouble();
bookmark.totalTimeInSeconds = m_pDS->fv("totalTimeInSeconds").get_asDouble();
bookmark.thumbNailImage = m_pDS->fv("thumbNailImage").get_asString();
bookmark.playerState = m_pDS->fv("playerState").get_asString();
bookmark.player = m_pDS->fv("player").get_asString();
bookmark.type = (CBookmark::EType)m_pDS->fv("type").get_asInt();
}
else
{
m_pDS->close();
return false;
}
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
return false;
}
return true;
}
void CVideoDatabase::AddBookMarkForEpisode(const CVideoInfoTag& tag, const CBookmark& bookmark)
{
try
{
int idFile = GetFileId(tag.m_strFileNameAndPath);
// delete the current episode for the selected episode number
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);
m_pDS->exec(strSQL.c_str());
AddBookMarkToFile(tag.m_strFileNameAndPath, bookmark, CBookmark::EPISODE);
int idBookmark = (int)m_pDS->lastinsertid();
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);
m_pDS->exec(strSQL.c_str());
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
}
}
void CVideoDatabase::DeleteBookMarkForEpisode(const CVideoInfoTag& tag)
{
try
{
CStdString strSQL = PrepareSQL("delete from bookmark where idBookmark in (select c%02d from episode where idEpisode=%i)", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
m_pDS->exec(strSQL.c_str());
strSQL = PrepareSQL("update episode set c%02d=-1 where idEpisode=%i", VIDEODB_ID_EPISODE_BOOKMARK, tag.m_iDbId);
m_pDS->exec(strSQL.c_str());
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, tag.m_iDbId);
}
}
//********************************************************************************************************************************
void CVideoDatabase::DeleteMovie(int idMovie, bool bKeepId /* = false */)
{
if (idMovie < 0)
return;
CStdString path;
GetFilePathById(idMovie, path, VIDEODB_CONTENT_MOVIES);
if (!path.empty())
DeleteMovie(path, bKeepId, idMovie);
}
void CVideoDatabase::DeleteMovie(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMovie /* = -1 */)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
if (idMovie < 0)
{
idMovie = GetMovieId(strFilenameAndPath);
if (idMovie < 0)
return;
}
BeginTransaction();
CStdString strSQL;
strSQL=PrepareSQL("delete from genrelinkmovie where idMovie=%i", idMovie);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from actorlinkmovie where idMovie=%i", idMovie);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from directorlinkmovie where idMovie=%i", idMovie);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from studiolinkmovie where idMovie=%i", idMovie);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from setlinkmovie where idMovie=%i", idMovie);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from countrylinkmovie where idMovie=%i", idMovie);
m_pDS->exec(strSQL.c_str());
DeleteStreamDetails(GetFileId(strFilenameAndPath));
// keep the movie table entry, linking to tv shows, and bookmarks
// so we can update the data in place
// the ancilliary tables are still purged
if (!bKeepId)
{
ClearBookMarksOfFile(strFilenameAndPath);
strSQL=PrepareSQL("delete from movie where idMovie=%i", idMovie);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from movielinktvshow where idMovie=%i", idMovie);
m_pDS->exec(strSQL.c_str());
}
CStdString strPath, strFileName;
SplitPath(strFilenameAndPath,strPath,strFileName);
InvalidatePathHash(strPath);
CommitTransaction();
if (!bKeepId)
AnnounceRemove("movie", idMovie);
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
}
}
void CVideoDatabase::DeleteTvShow(int idTvShow, bool bKeepId /* = false */)
{
if (idTvShow < 0)
return;
CStdString path;
GetFilePathById(idTvShow, path, VIDEODB_CONTENT_TVSHOWS);
if (!path.empty())
DeleteTvShow(path, bKeepId, idTvShow);
}
void CVideoDatabase::DeleteTvShow(const CStdString& strPath, bool bKeepId /* = false */, int idTvShow /* = -1 */)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
if (idTvShow < 0)
{
idTvShow = GetTvShowId(strPath);
if (idTvShow < 0)
return;
}
BeginTransaction();
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);
m_pDS2->query(strSQL.c_str());
while (!m_pDS2->eof())
{
CStdString strFilenameAndPath;
CStdString strPath = m_pDS2->fv("path.strPath").get_asString();
CStdString strFileName = m_pDS2->fv("files.strFilename").get_asString();
ConstructPath(strFilenameAndPath, strPath, strFileName);
DeleteEpisode(strFilenameAndPath, m_pDS2->fv(0).get_asInt(), bKeepId);
m_pDS2->next();
}
DeleteDetailsForTvShow(strPath, idTvShow);
strSQL=PrepareSQL("delete from seasons where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
// keep tvshow table and movielink table so we can update data in place
if (!bKeepId)
{
strSQL=PrepareSQL("delete from tvshow where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from tvshowlinkpath where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from movielinktvshow where idShow=%i", idTvShow);
m_pDS->exec(strSQL.c_str());
}
InvalidatePathHash(strPath);
CommitTransaction();
if (!bKeepId)
AnnounceRemove("tvshow", idTvShow);
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strPath.c_str());
}
}
void CVideoDatabase::DeleteEpisode(int idEpisode, bool bKeepId /* = false */)
{
if (idEpisode < 0)
return;
CStdString path;
GetFilePathById(idEpisode, path, VIDEODB_CONTENT_EPISODES);
if (!path.empty())
DeleteEpisode(path, idEpisode, bKeepId);
}
void CVideoDatabase::DeleteEpisode(const CStdString& strFilenameAndPath, int idEpisode /* = -1 */, bool bKeepId /* = false */)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
if (idEpisode < 0)
{
idEpisode = GetEpisodeId(strFilenameAndPath);
if (idEpisode < 0)
{
return ;
}
}
CStdString strSQL;
strSQL=PrepareSQL("delete from actorlinkepisode where idEpisode=%i", idEpisode);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from directorlinkepisode where idEpisode=%i", idEpisode);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from writerlinkepisode where idEpisode=%i", idEpisode);
m_pDS->exec(strSQL.c_str());
DeleteStreamDetails(GetFileId(strFilenameAndPath));
// keep episode table entry and bookmarks so we can update the data in place
// the ancilliary tables are still purged
if (!bKeepId)
{
ClearBookMarksOfFile(strFilenameAndPath);
strSQL=PrepareSQL("delete from episode where idEpisode=%i", idEpisode);
m_pDS->exec(strSQL.c_str());
}
if (!bKeepId)
AnnounceRemove("episode", idEpisode);
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
}
void CVideoDatabase::DeleteMusicVideo(int idMusicVideo, bool bKeepId /* = false */)
{
if (idMusicVideo < 0)
return;
CStdString path;
GetFilePathById(idMusicVideo, path, VIDEODB_CONTENT_MUSICVIDEOS);
if (!path.empty())
DeleteMusicVideo(path, bKeepId, idMusicVideo);
}
void CVideoDatabase::DeleteMusicVideo(const CStdString& strFilenameAndPath, bool bKeepId /* = false */, int idMVideo /* = -1 */)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
if (idMVideo < 0)
{
idMVideo = GetMusicVideoId(strFilenameAndPath);
if (idMVideo < 0)
return;
}
BeginTransaction();
CStdString strSQL;
strSQL=PrepareSQL("delete from genrelinkmusicvideo where idMVideo=%i", idMVideo);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from artistlinkmusicvideo where idMVideo=%i", idMVideo);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from directorlinkmusicvideo where idMVideo=%i", idMVideo);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from studiolinkmusicvideo where idMVideo=%i", idMVideo);
m_pDS->exec(strSQL.c_str());
DeleteStreamDetails(GetFileId(strFilenameAndPath));
// keep the music video table entry and bookmarks so we can update data in place
// the ancilliary tables are still purged
if (!bKeepId)
{
ClearBookMarksOfFile(strFilenameAndPath);
strSQL=PrepareSQL("delete from musicvideo where idMVideo=%i", idMVideo);
m_pDS->exec(strSQL.c_str());
}
CStdString strPath, strFileName;
SplitPath(strFilenameAndPath,strPath,strFileName);
InvalidatePathHash(strPath);
CommitTransaction();
if (!bKeepId)
AnnounceRemove("musicvideo", idMVideo);
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
}
}
void CVideoDatabase::DeleteStreamDetails(int idFile)
{
m_pDS->exec(PrepareSQL("delete from streamdetails where idFile=%i", idFile));
}
void CVideoDatabase::DeleteSet(int idSet)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
CStdString strSQL;
strSQL=PrepareSQL("delete from sets where idSet=%i", idSet);
m_pDS->exec(strSQL.c_str());
strSQL=PrepareSQL("delete from setlinkmovie where idSet=%i", idSet);
m_pDS->exec(strSQL.c_str());
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%i) failed", __FUNCTION__, idSet);
}
}
void CVideoDatabase::GetDetailsFromDB(auto_ptr<Dataset> &pDS, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
{
GetDetailsFromDB(pDS->get_sql_record(), min, max, offsets, details, idxOffset);
}
void CVideoDatabase::GetDetailsFromDB(const dbiplus::sql_record* const record, int min, int max, const SDbTableOffsets *offsets, CVideoInfoTag &details, int idxOffset)
{
for (int i = min + 1; i < max; i++)
{
switch (offsets[i].type)
{
case VIDEODB_TYPE_STRING:
*(CStdString*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asString();
break;
case VIDEODB_TYPE_INT:
case VIDEODB_TYPE_COUNT:
*(int*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asInt();
break;
case VIDEODB_TYPE_BOOL:
*(bool*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asBool();
break;
case VIDEODB_TYPE_FLOAT:
*(float*)(((char*)&details)+offsets[i].offset) = record->at(i+idxOffset).get_asFloat();
break;
case VIDEODB_TYPE_STRINGARRAY:
*(std::vector<std::string>*)(((char*)&details)+offsets[i].offset) = StringUtils::Split(record->at(i+idxOffset).get_asString(), g_advancedSettings.m_videoItemSeparator);
break;
case VIDEODB_TYPE_DATE:
((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDate(record->at(i+idxOffset).get_asString());
break;
case VIDEODB_TYPE_DATETIME:
((CDateTime*)(((char*)&details)+offsets[i].offset))->SetFromDBDateTime(record->at(i+idxOffset).get_asString());
break;
}
}
}
DWORD movieTime = 0;
DWORD castTime = 0;
CVideoInfoTag CVideoDatabase::GetDetailsByTypeAndId(VIDEODB_CONTENT_TYPE type, int id)
{
CVideoInfoTag details;
details.Reset();
switch (type)
{
case VIDEODB_CONTENT_MOVIES:
GetMovieInfo("", details, id);
break;
case VIDEODB_CONTENT_TVSHOWS:
GetTvShowInfo("", details, id);
break;
case VIDEODB_CONTENT_EPISODES:
GetEpisodeInfo("", details, id);
break;
case VIDEODB_CONTENT_MUSICVIDEOS:
GetMusicVideoInfo("", details, id);
default:
break;
}
return details;
}
bool CVideoDatabase::GetStreamDetails(CVideoInfoTag& tag) const
{
if (tag.m_iFileId < 0)
return false;
bool retVal = false;
auto_ptr<Dataset> pDS(m_pDB->CreateDataset());
CStdString strSQL = PrepareSQL("SELECT * FROM streamdetails WHERE idFile = %i", tag.m_iFileId);
pDS->query(strSQL);
CStreamDetails& details = tag.m_streamDetails;
details.Reset();
while (!pDS->eof())
{
CStreamDetail::StreamType e = (CStreamDetail::StreamType)pDS->fv(1).get_asInt();
switch (e)
{
case CStreamDetail::VIDEO:
{
CStreamDetailVideo *p = new CStreamDetailVideo();
p->m_strCodec = pDS->fv(2).get_asString();
p->m_fAspect = pDS->fv(3).get_asFloat();
p->m_iWidth = pDS->fv(4).get_asInt();
p->m_iHeight = pDS->fv(5).get_asInt();
p->m_iDuration = pDS->fv(10).get_asInt();
details.AddStream(p);
retVal = true;
break;
}
case CStreamDetail::AUDIO:
{
CStreamDetailAudio *p = new CStreamDetailAudio();
p->m_strCodec = pDS->fv(6).get_asString();
if (pDS->fv(7).get_isNull())
p->m_iChannels = -1;
else
p->m_iChannels = pDS->fv(7).get_asInt();
p->m_strLanguage = pDS->fv(8).get_asString();
details.AddStream(p);
retVal = true;
break;
}
case CStreamDetail::SUBTITLE:
{
CStreamDetailSubtitle *p = new CStreamDetailSubtitle();
p->m_strLanguage = pDS->fv(9).get_asString();
details.AddStream(p);
retVal = true;
break;
}
}
pDS->next();
}
pDS->close();
details.DetermineBestStreams();
if (details.GetVideoDuration() > 0)
tag.m_strRuntime.Format("%i", details.GetVideoDuration() / 60 );
return retVal;
}
bool CVideoDatabase::GetResumePoint(CVideoInfoTag& tag) const
{
if (tag.m_iFileId < 0)
return false;
bool match = false;
try
{
CStdString strSQL=PrepareSQL("select timeInSeconds, totalTimeInSeconds from bookmark where idFile=%i and type=%i order by timeInSeconds", tag.m_iFileId, CBookmark::RESUME);
m_pDS2->query( strSQL.c_str() );
if (!m_pDS2->eof())
{
tag.m_resumePoint.timeInSeconds = m_pDS2->fv(0).get_asDouble();
tag.m_resumePoint.totalTimeInSeconds = m_pDS2->fv(1).get_asDouble();
tag.m_resumePoint.type = CBookmark::RESUME;
match = true;
}
m_pDS2->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, tag.m_strFileNameAndPath.c_str());
}
return match;
}
CVideoInfoTag CVideoDatabase::GetDetailsForMovie(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
{
return GetDetailsForMovie(pDS->get_sql_record(), needsCast);
}
CVideoInfoTag CVideoDatabase::GetDetailsForMovie(const dbiplus::sql_record* const record, bool needsCast /* = false */)
{
CVideoInfoTag details;
if (record == NULL)
return details;
DWORD time = XbmcThreads::SystemClockMillis();
int idMovie = record->at(0).get_asInt();
GetDetailsFromDB(record, VIDEODB_ID_MIN, VIDEODB_ID_MAX, DbMovieOffsets, details);
details.m_iDbId = idMovie;
details.m_type = "movie";
GetCommonDetails(record, details);
movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
GetStreamDetails(details);
if (needsCast)
{
GetResumePoint(details);
GetCast("movie", "idMovie", details.m_iDbId, details.m_cast);
castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
details.m_strPictureURL.Parse();
// create sets string
CStdString strSQL = PrepareSQL("SELECT sets.idSet, sets.strSet FROM sets,setlinkmovie WHERE setlinkmovie.idMovie=%i AND setlinkmovie.idSet=sets.idSet ORDER BY sets.idSet",idMovie);
m_pDS2->query(strSQL.c_str());
while (!m_pDS2->eof())
{
details.m_set.push_back(m_pDS2->fv("sets.strSet").get_asString());
details.m_setId.push_back(m_pDS2->fv("sets.idSet").get_asInt());
m_pDS2->next();
}
// create tvshowlink string
vector<int> links;
GetLinksToTvShow(idMovie,links);
for (unsigned int i=0;i<links.size();++i)
{
strSQL = PrepareSQL("select c%02d from tvshow where idShow=%i",
VIDEODB_ID_TV_TITLE,links[i]);
m_pDS2->query(strSQL.c_str());
if (!m_pDS2->eof())
details.m_showLink.push_back(m_pDS2->fv(0).get_asString());
}
m_pDS2->close();
}
return details;
}
CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
{
return GetDetailsForTvShow(pDS->get_sql_record(), needsCast);
}
CVideoInfoTag CVideoDatabase::GetDetailsForTvShow(const dbiplus::sql_record* const record, bool needsCast /* = false */)
{
CVideoInfoTag details;
if (record == NULL)
return details;
DWORD time = XbmcThreads::SystemClockMillis();
int idTvShow = record->at(0).get_asInt();
GetDetailsFromDB(record, VIDEODB_ID_TV_MIN, VIDEODB_ID_TV_MAX, DbTvShowOffsets, details, 1);
details.m_iDbId = idTvShow;
details.m_type = "tvshow";
details.m_strPath = record->at(VIDEODB_DETAILS_TVSHOW_PATH).get_asString();
details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_TVSHOW_DATEADDED).get_asString());
details.m_iEpisode = record->at(VIDEODB_DETAILS_TVSHOW_NUM_EPISODES).get_asInt();
details.m_playCount = record->at(VIDEODB_DETAILS_TVSHOW_NUM_WATCHED).get_asInt();
details.m_strShowPath = details.m_strPath;
details.m_strShowTitle = details.m_strTitle;
movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
if (needsCast)
{
GetCast("tvshow", "idShow", details.m_iDbId, details.m_cast);
castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
details.m_strPictureURL.Parse();
}
return details;
}
CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(auto_ptr<Dataset> &pDS, bool needsCast /* = false */)
{
return GetDetailsForEpisode(pDS->get_sql_record(), needsCast);
}
CVideoInfoTag CVideoDatabase::GetDetailsForEpisode(const dbiplus::sql_record* const record, bool needsCast /* = false */)
{
CVideoInfoTag details;
if (record == NULL)
return details;
DWORD time = XbmcThreads::SystemClockMillis();
int idEpisode = record->at(0).get_asInt();
GetDetailsFromDB(record, VIDEODB_ID_EPISODE_MIN, VIDEODB_ID_EPISODE_MAX, DbEpisodeOffsets, details);
details.m_iDbId = idEpisode;
details.m_type = "episode";
details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
details.m_strPath = record->at(VIDEODB_DETAILS_EPISODE_PATH).get_asString();
CStdString strFileName = record->at(VIDEODB_DETAILS_EPISODE_FILE).get_asString();
ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
details.m_playCount = record->at(VIDEODB_DETAILS_EPISODE_PLAYCOUNT).get_asInt();
details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_LASTPLAYED).get_asString());
details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_EPISODE_DATEADDED).get_asString());
details.m_strMPAARating = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_MPAA).get_asString();
details.m_strShowTitle = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_NAME).get_asString();
details.m_studio = StringUtils::Split(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_STUDIO).get_asString(), g_advancedSettings.m_videoItemSeparator);
details.m_premiered.SetFromDBDate(record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_AIRED).get_asString());
details.m_iIdShow = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_ID).get_asInt();
details.m_strShowPath = record->at(VIDEODB_DETAILS_EPISODE_TVSHOW_PATH).get_asString();
movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
GetStreamDetails(details);
if (needsCast)
{
GetResumePoint(details);
GetCast("episode", "idEpisode", details.m_iDbId, details.m_cast);
GetCast("tvshow", "idShow", details.m_iIdShow, details.m_cast);
castTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
details.m_strPictureURL.Parse();
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);
m_pDS2->query(strSQL.c_str());
if (!m_pDS2->eof())
details.m_fEpBookmark = m_pDS2->fv("bookmark.timeInSeconds").get_asFloat();
m_pDS2->close();
}
return details;
}
CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(auto_ptr<Dataset> &pDS)
{
return GetDetailsForMusicVideo(pDS->get_sql_record());
}
CVideoInfoTag CVideoDatabase::GetDetailsForMusicVideo(const dbiplus::sql_record* const record)
{
CVideoInfoTag details;
unsigned int time = XbmcThreads::SystemClockMillis();
int idMovie = record->at(0).get_asInt();
GetDetailsFromDB(record, VIDEODB_ID_MUSICVIDEO_MIN, VIDEODB_ID_MUSICVIDEO_MAX, DbMusicVideoOffsets, details);
details.m_iDbId = idMovie;
details.m_type = "musicvideo";
GetCommonDetails(record, details);
movieTime += XbmcThreads::SystemClockMillis() - time; time = XbmcThreads::SystemClockMillis();
GetStreamDetails(details);
GetResumePoint(details);
details.m_strPictureURL.Parse();
return details;
}
void CVideoDatabase::GetCommonDetails(auto_ptr<Dataset> &pDS, CVideoInfoTag &details)
{
GetCommonDetails(pDS->get_sql_record(), details);
}
void CVideoDatabase::GetCommonDetails(const dbiplus::sql_record* const record, CVideoInfoTag &details)
{
details.m_iFileId = record->at(VIDEODB_DETAILS_FILEID).get_asInt();
details.m_strPath = record->at(VIDEODB_DETAILS_PATH).get_asString();
CStdString strFileName = record->at(VIDEODB_DETAILS_FILE).get_asString();
ConstructPath(details.m_strFileNameAndPath,details.m_strPath,strFileName);
details.m_playCount = record->at(VIDEODB_DETAILS_PLAYCOUNT).get_asInt();
details.m_lastPlayed.SetFromDBDateTime(record->at(VIDEODB_DETAILS_LASTPLAYED).get_asString());
details.m_dateAdded.SetFromDBDateTime(record->at(VIDEODB_DETAILS_DATEADDED).get_asString());
}
void CVideoDatabase::GetCast(const CStdString &table, const CStdString &table_id, int type_id, vector<SActorInfo> &cast)
{
try
{
if (!m_pDB.get()) return;
if (!m_pDS2.get()) return;
CStdString sql = PrepareSQL("SELECT actors.strActor,"
" actorlink%s.strRole,"
" actors.strThumb,"
" art.url "
"FROM actorlink%s"
" JOIN actors ON"
" actorlink%s.idActor=actors.idActor"
" LEFT JOIN art ON"
" art.media_id=actors.idActor AND art.media_type='actor' AND art.type='thumb' "
"WHERE actorlink%s.%s=%i "
"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());
m_pDS2->query(sql.c_str());
while (!m_pDS2->eof())
{
SActorInfo info;
info.strName = m_pDS2->fv(0).get_asString();
bool found = false;
for (vector<SActorInfo>::iterator i = cast.begin(); i != cast.end(); ++i)
{
if (i->strName == info.strName)
{
found = true;
break;
}
}
if (!found)
{
info.strRole = m_pDS2->fv(1).get_asString();
info.thumbUrl.ParseString(m_pDS2->fv(2).get_asString());
info.thumb = m_pDS2->fv(3).get_asString();
cast.push_back(info);
}
m_pDS2->next();
}
m_pDS2->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s(%s,%s,%i) failed", __FUNCTION__, table.c_str(), table_id.c_str(), type_id);
}
}
/// \brief GetVideoSettings() obtains any saved video settings for the current file.
/// \retval Returns true if the settings exist, false otherwise.
bool CVideoDatabase::GetVideoSettings(const CStdString &strFilenameAndPath, CVideoSettings &settings)
{
try
{
// obtain the FileID (if it exists)
#ifdef NEW_VIDEODB_METHODS
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
CStdString strPath, strFileName;
URIUtils::Split(strFilenameAndPath, strPath, strFileName);
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());
#else
int idFile = GetFileId(strFilenameAndPath);
if (idFile < 0) return false;
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
// ok, now obtain the settings for this file
CStdString strSQL=PrepareSQL("select * from settings where settings.idFile = '%i'", idFile);
#endif
m_pDS->query( strSQL.c_str() );
if (m_pDS->num_rows() > 0)
{ // get the video settings info
settings.m_AudioDelay = m_pDS->fv("AudioDelay").get_asFloat();
settings.m_AudioStream = m_pDS->fv("AudioStream").get_asInt();
settings.m_Brightness = m_pDS->fv("Brightness").get_asFloat();
settings.m_Contrast = m_pDS->fv("Contrast").get_asFloat();
settings.m_CustomPixelRatio = m_pDS->fv("PixelRatio").get_asFloat();
settings.m_CustomNonLinStretch = m_pDS->fv("NonLinStretch").get_asBool();
settings.m_NoiseReduction = m_pDS->fv("NoiseReduction").get_asFloat();
settings.m_PostProcess = m_pDS->fv("PostProcess").get_asBool();
settings.m_Sharpness = m_pDS->fv("Sharpness").get_asFloat();
settings.m_CustomZoomAmount = m_pDS->fv("ZoomAmount").get_asFloat();
settings.m_CustomVerticalShift = m_pDS->fv("VerticalShift").get_asFloat();
settings.m_Gamma = m_pDS->fv("Gamma").get_asFloat();
settings.m_SubtitleDelay = m_pDS->fv("SubtitleDelay").get_asFloat();
settings.m_SubtitleOn = m_pDS->fv("SubtitlesOn").get_asBool();
settings.m_SubtitleStream = m_pDS->fv("SubtitleStream").get_asInt();
settings.m_ViewMode = m_pDS->fv("ViewMode").get_asInt();
settings.m_ResumeTime = m_pDS->fv("ResumeTime").get_asInt();
settings.m_Crop = m_pDS->fv("Crop").get_asBool();
settings.m_CropLeft = m_pDS->fv("CropLeft").get_asInt();
settings.m_CropRight = m_pDS->fv("CropRight").get_asInt();
settings.m_CropTop = m_pDS->fv("CropTop").get_asInt();
settings.m_CropBottom = m_pDS->fv("CropBottom").get_asInt();
settings.m_DeinterlaceMode = (EDEINTERLACEMODE)m_pDS->fv("DeinterlaceMode").get_asInt();
settings.m_InterlaceMethod = (EINTERLACEMETHOD)m_pDS->fv("Deinterlace").get_asInt();
settings.m_VolumeAmplification = m_pDS->fv("VolumeAmplification").get_asFloat();
settings.m_OutputToAllSpeakers = m_pDS->fv("OutputToAllSpeakers").get_asBool();
settings.m_ScalingMethod = (ESCALINGMETHOD)m_pDS->fv("ScalingMethod").get_asInt();
settings.m_SubtitleCached = false;
m_pDS->close();
return true;
}
m_pDS->close();
}
catch (...)
{
CLog::Log(LOGERROR, "%s failed", __FUNCTION__);
}
return false;
}
/// \brief Sets the settings for a particular video file
void CVideoDatabase::SetVideoSettings(const CStdString& strFilenameAndPath, const CVideoSettings &setting)
{
try
{
if (NULL == m_pDB.get()) return ;
if (NULL == m_pDS.get()) return ;
int idFile = AddFile(strFilenameAndPath);
if (idFile < 0)
return;
CStdString strSQL;
strSQL.Format("select * from settings where idFile=%i", idFile);
m_pDS->query( strSQL.c_str() );
if (m_pDS->num_rows() > 0)
{
m_pDS->close();
// update the item
strSQL=PrepareSQL("update settings set Deinterlace=%i,ViewMode=%i,ZoomAmount=%f,PixelRatio=%f,VerticalShift=%f,"
"AudioStream=%i,SubtitleStream=%i,SubtitleDelay=%f,SubtitlesOn=%i,Brightness=%f,Contrast=%f,Gamma=%f,"
"VolumeAmplification=%f,AudioDelay=%f,OutputToAllSpeakers=%i,Sharpness=%f,NoiseReduction=%f,NonLinStretch=%i,PostProcess=%i,ScalingMethod=%i,"
"DeinterlaceMode=%i,",
setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn,
setting.m_Brightness, setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay,
setting.m_OutputToAllSpeakers,setting.m_Sharpness,setting.m_NoiseReduction,setting.m_CustomNonLinStretch,setting.m_PostProcess,setting.m_ScalingMethod,
setting.m_DeinterlaceMode);
CStdString strSQL2;
strSQL2=PrepareSQL("ResumeTime=%i,Crop=%i,CropLeft=%i,CropRight=%i,CropTop=%i,CropBottom=%i where idFile=%i\n", setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom, idFile);
strSQL += strSQL2;
m_pDS->exec(strSQL.c_str());
return ;
}
else
{ // add the items
m_pDS->close();
strSQL= "INSERT INTO settings (idFile,Deinterlace,ViewMode,ZoomAmount,PixelRatio, VerticalShift, "
"AudioStream,SubtitleStream,SubtitleDelay,SubtitlesOn,Brightness,"
"Contrast,Gamma,VolumeAmplification,AudioDelay,OutputToAllSpeakers,"
"ResumeTime,Crop,CropLeft,CropRight,CropTop,CropBottom,"
"Sharpness,NoiseReduction,NonLinStretch,PostProcess,ScalingMethod,DeinterlaceMode) "
"VALUES ";
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)",
idFile, setting.m_InterlaceMethod, setting.m_ViewMode, setting.m_CustomZoomAmount, setting.m_CustomPixelRatio, setting.m_CustomVerticalShift,
setting.m_AudioStream, setting.m_SubtitleStream, setting.m_SubtitleDelay, setting.m_SubtitleOn, setting.m_Brightness,
setting.m_Contrast, setting.m_Gamma, setting.m_VolumeAmplification, setting.m_AudioDelay, setting.m_OutputToAllSpeakers,
setting.m_ResumeTime, setting.m_Crop, setting.m_CropLeft, setting.m_CropRight, setting.m_CropTop, setting.m_CropBottom,
setting.m_Sharpness, setting.m_NoiseReduction, setting.m_CustomNonLinStretch, setting.m_PostProcess, setting.m_ScalingMethod,
setting.m_DeinterlaceMode);
m_pDS->exec(strSQL.c_str());
}
}
catch (...)
{
CLog::Log(LOGERROR, "%s (%s) failed", __FUNCTION__, strFilenameAndPath.c_str());
}
}
void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const map<string, string> &art)
{
for (map<string, string>::const_iterator i = art.begin(); i != art.end(); ++i)
SetArtForItem(mediaId, mediaType, i->first, i->second);
}
void CVideoDatabase::SetArtForItem(int mediaId, const string &mediaType, const string &artType, const string &url)
{
try
{
if (NULL == m_pDB.get()) return;
if (NULL == m_pDS.get()) return;
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());
m_pDS->query(sql.c_str());
if (!m_pDS->eof())
{ // update
int artId = m_pDS->fv(0).get_asInt();
m_pDS->close();
sql = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", url.c_str(), artId);
m_pDS->exec(sql.c_str());
}
else
{ // insert
m_pDS->close();
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());
m_pDS->exec(sql.c_str());
}
}
catch (...)
{
CLog::Log(LOGERROR, "%s(%d, '%s', '%s', '%s') failed", __FUNCTION__, mediaId, mediaType.c_str(), artType.c_str(), url.c_str());
}
}
bool CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, map<string, string> &art)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
CStdString sql = PrepareSQL("SELECT type,url FROM art WHERE media_id=%i AND media_type='%s'", mediaId, mediaType.c_str());
m_pDS2->query(sql.c_str());
while (!m_pDS2->eof())
{
art.insert(make_pair(m_pDS2->fv(0).get_asString(), m_pDS2->fv(1).get_asString()));
m_pDS2->next();
}
m_pDS2->close();
return !art.empty();
}
catch (...)
{
CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, mediaId);
}
return false;
}
string CVideoDatabase::GetArtForItem(int mediaId, const string &mediaType, const string &artType)
{
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());
return GetSingleValue(query, m_pDS2);
}
bool CVideoDatabase::GetTvShowSeasonArt(int showId, map<int, string> &seasonArt)
{
try
{
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS2.get()) return false; // using dataset 2 as we're likely called in loops on dataset 1
// get all seasons for this show
CStdString sql = PrepareSQL("select idSeason,season from seasons where idShow=%i", showId);
m_pDS2->query(sql.c_str());
vector< pair<int, int> > seasons;
while (!m_pDS2->eof())
{
seasons.push_back(make_pair(m_pDS2->fv(0).get_asInt(), m_pDS2->fv(1).get_asInt()));
m_pDS2->next();
}
m_pDS2->close();
for (vector< pair<int,int> >::const_iterator i = seasons.begin(); i != seasons.end(); ++i)
seasonArt.insert(make_pair(i->second,GetArtForItem(i->first, "season", "thumb")));
}
catch (...)
{
CLog::Log(LOGERROR, "%s(%d) failed", __FUNCTION__, showId);
}
return false;
}
/// \brief GetStackTimes() obtains any saved video times for the stacked file
/// \retval Returns true if the stack times exist, false otherwise.
bool CVideoDatabase::GetStackTimes(const CStdString &filePath, vector<int> &times)
{
try
{
// obtain the FileID (if it exists)
int idFile = GetFileId(filePath);
if (idFile < 0) return false;
if (NULL == m_pDB.get()) return false;
if (NULL == m_pDS.get()) return false;
// ok, now obtain the settings for this file
CStdString strSQL=PrepareSQL("select times from stacktimes where idFile=%i\n", idFile);
m_pDS->query( strSQL.c_str() );