diff options
Diffstat (limited to 'xbmc/interfaces/json-rpc/VideoLibrary.cpp')
-rw-r--r-- | xbmc/interfaces/json-rpc/VideoLibrary.cpp | 1385 |
1 files changed, 1385 insertions, 0 deletions
diff --git a/xbmc/interfaces/json-rpc/VideoLibrary.cpp b/xbmc/interfaces/json-rpc/VideoLibrary.cpp new file mode 100644 index 0000000..db152cd --- /dev/null +++ b/xbmc/interfaces/json-rpc/VideoLibrary.cpp @@ -0,0 +1,1385 @@ +/* + * Copyright (C) 2016-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "VideoLibrary.h" + +#include "FileItem.h" +#include "PVROperations.h" +#include "ServiceBroker.h" +#include "TextureDatabase.h" +#include "Util.h" +#include "messaging/ApplicationMessenger.h" +#include "utils/SortUtils.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "video/VideoDatabase.h" +#include "video/VideoDbUrl.h" +#include "video/VideoLibraryQueue.h" + +using namespace JSONRPC; + +JSONRPC_STATUS CVideoLibrary::GetMovies(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + SortDescription sorting; + ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd); + if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes)) + return InvalidParams; + + CVideoDbUrl videoUrl; + if (!videoUrl.FromString("videodb://movies/titles/")) + return InternalError; + + int genreID = -1, year = -1, setID = 0; + const CVariant &filter = parameterObject["filter"]; + if (filter.isMember("genreid")) + genreID = (int)filter["genreid"].asInteger(); + else if (filter.isMember("genre")) + videoUrl.AddOption("genre", filter["genre"].asString()); + else if (filter.isMember("year")) + year = (int)filter["year"].asInteger(); + else if (filter.isMember("actor")) + videoUrl.AddOption("actor", filter["actor"].asString()); + else if (filter.isMember("director")) + videoUrl.AddOption("director", filter["director"].asString()); + else if (filter.isMember("studio")) + videoUrl.AddOption("studio", filter["studio"].asString()); + else if (filter.isMember("country")) + videoUrl.AddOption("country", filter["country"].asString()); + else if (filter.isMember("setid")) + setID = (int)filter["setid"].asInteger(); + else if (filter.isMember("set")) + videoUrl.AddOption("set", filter["set"].asString()); + else if (filter.isMember("tag")) + videoUrl.AddOption("tag", filter["tag"].asString()); + else if (filter.isObject()) + { + std::string xsp; + if (!GetXspFiltering("movies", filter, xsp)) + return InvalidParams; + + videoUrl.AddOption("xsp", xsp); + } + + // setID must not be -1 otherwise GetMoviesNav() will return sets + if (setID < 0) + setID = 0; + + CFileItemList items; + if (!videodatabase.GetMoviesNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, setID, -1, sorting, RequiresAdditionalDetails(MediaTypeMovie, parameterObject))) + return InvalidParams; + + return HandleItems("movieid", "movies", items, parameterObject, result, false); +} + +JSONRPC_STATUS CVideoLibrary::GetMovieDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["movieid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + if (!videodatabase.GetMovieInfo("", infos, id, RequiresAdditionalDetails(MediaTypeMovie, parameterObject)) || infos.m_iDbId <= 0) + return InvalidParams; + + HandleFileItem("movieid", true, "moviedetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false); + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetMovieSets(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CFileItemList items; + if (!videodatabase.GetSetsNav("videodb://movies/sets/", items, VideoDbContentType::MOVIES)) + return InternalError; + + HandleFileItemList("setid", false, "sets", items, parameterObject, result); + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetMovieSetDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["setid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + // Get movie set details + CVideoInfoTag infos; + if (!videodatabase.GetSetInfo(id, infos) || infos.m_iDbId <= 0) + return InvalidParams; + + HandleFileItem("setid", false, "setdetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false); + + // Get movies from the set + CFileItemList items; + if (!videodatabase.GetMoviesNav("videodb://movies/titles/", items, -1, -1, -1, -1, -1, -1, id, -1, SortDescription(), RequiresAdditionalDetails(MediaTypeMovie, parameterObject["movies"]))) + return InternalError; + + return HandleItems("movieid", "movies", items, parameterObject["movies"], result["setdetails"], true); +} + +JSONRPC_STATUS CVideoLibrary::GetTVShows(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + SortDescription sorting; + ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd); + if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes)) + return InvalidParams; + + CVideoDbUrl videoUrl; + if (!videoUrl.FromString("videodb://tvshows/titles/")) + return InternalError; + + const CVariant &filter = parameterObject["filter"]; + if (filter.isMember("genreid")) + videoUrl.AddOption("genreid", (int)filter["genreid"].asInteger()); + else if (filter.isMember("genre")) + videoUrl.AddOption("genre", filter["genre"].asString()); + else if (filter.isMember("year")) + videoUrl.AddOption("year", (int)filter["year"].asInteger()); + else if (filter.isMember("actor")) + videoUrl.AddOption("actor", filter["actor"].asString()); + else if (filter.isMember("studio")) + videoUrl.AddOption("studio", filter["studio"].asString()); + else if (filter.isMember("tag")) + videoUrl.AddOption("tag", filter["tag"].asString()); + else if (filter.isObject()) + { + std::string xsp; + if (!GetXspFiltering("tvshows", filter, xsp)) + return InvalidParams; + + videoUrl.AddOption("xsp", xsp); + } + + CFileItemList items; + CDatabase::Filter nofilter; + if (!videodatabase.GetTvShowsByWhere(videoUrl.ToString(), nofilter, items, sorting, RequiresAdditionalDetails(MediaTypeTvShow, parameterObject))) + return InvalidParams; + + return HandleItems("tvshowid", "tvshows", items, parameterObject, result, false); +} + +JSONRPC_STATUS CVideoLibrary::GetTVShowDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + int id = (int)parameterObject["tvshowid"].asInteger(); + + CFileItemPtr fileItem(new CFileItem()); + CVideoInfoTag infos; + if (!videodatabase.GetTvShowInfo("", infos, id, fileItem.get(), RequiresAdditionalDetails(MediaTypeTvShow, parameterObject)) || infos.m_iDbId <= 0) + return InvalidParams; + + fileItem->SetFromVideoInfoTag(infos); + HandleFileItem("tvshowid", true, "tvshowdetails", fileItem, parameterObject, parameterObject["properties"], result, false); + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetSeasons(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + int tvshowID = (int)parameterObject["tvshowid"].asInteger(); + + std::string strPath = StringUtils::Format("videodb://tvshows/titles/{}/", tvshowID); + CFileItemList items; + if (!videodatabase.GetSeasonsNav(strPath, items, -1, -1, -1, -1, tvshowID, false)) + return InternalError; + + HandleFileItemList("seasonid", false, "seasons", items, parameterObject, result); + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetSeasonDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + int id = (int)parameterObject["seasonid"].asInteger(); + + CVideoInfoTag infos; + if (!videodatabase.GetSeasonInfo(id, infos) || + infos.m_iDbId <= 0 || infos.m_iIdShow <= 0) + return InvalidParams; + + CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos)); + HandleFileItem("seasonid", false, "seasondetails", pItem, parameterObject, parameterObject["properties"], result, false); + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetEpisodes(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + SortDescription sorting; + ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd); + if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes)) + return InvalidParams; + + int tvshowID = (int)parameterObject["tvshowid"].asInteger(); + int season = (int)parameterObject["season"].asInteger(); + + std::string strPath = StringUtils::Format("videodb://tvshows/titles/{}/{}/", tvshowID, season); + + CVideoDbUrl videoUrl; + if (!videoUrl.FromString(strPath)) + return InternalError; + + const CVariant &filter = parameterObject["filter"]; + if (filter.isMember("genreid")) + videoUrl.AddOption("genreid", (int)filter["genreid"].asInteger()); + else if (filter.isMember("genre")) + videoUrl.AddOption("genre", filter["genre"].asString()); + else if (filter.isMember("year")) + videoUrl.AddOption("year", (int)filter["year"].asInteger()); + else if (filter.isMember("actor")) + videoUrl.AddOption("actor", filter["actor"].asString()); + else if (filter.isMember("director")) + videoUrl.AddOption("director", filter["director"].asString()); + else if (filter.isObject()) + { + std::string xsp; + if (!GetXspFiltering("episodes", filter, xsp)) + return InvalidParams; + + videoUrl.AddOption("xsp", xsp); + } + + if (tvshowID <= 0 && (season > 0 || videoUrl.HasOption("genreid") || videoUrl.HasOption("genre") || videoUrl.HasOption("actor"))) + return InvalidParams; + + if (tvshowID > 0) + { + videoUrl.AddOption("tvshowid", tvshowID); + if (season >= 0) + videoUrl.AddOption("season", season); + } + + CFileItemList items; + if (!videodatabase.GetEpisodesByWhere(videoUrl.ToString(), CDatabase::Filter(), items, false, sorting, RequiresAdditionalDetails(MediaTypeEpisode, parameterObject))) + return InvalidParams; + + return HandleItems("episodeid", "episodes", items, parameterObject, result, false); +} + +JSONRPC_STATUS CVideoLibrary::GetEpisodeDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + int id = (int)parameterObject["episodeid"].asInteger(); + + CVideoInfoTag infos; + if (!videodatabase.GetEpisodeInfo("", infos, id, RequiresAdditionalDetails(MediaTypeEpisode, parameterObject)) || infos.m_iDbId <= 0) + return InvalidParams; + + CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos)); + // We need to set the correct base path to get the valid fanart + int tvshowid = infos.m_iIdShow; + if (tvshowid <= 0) + tvshowid = videodatabase.GetTvShowForEpisode(id); + + std::string basePath = + StringUtils::Format("videodb://tvshows/titles/{}/{}/{}", tvshowid, infos.m_iSeason, id); + pItem->SetPath(basePath); + + HandleFileItem("episodeid", true, "episodedetails", pItem, parameterObject, parameterObject["properties"], result, false); + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetMusicVideos(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + SortDescription sorting; + ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd); + if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes)) + return InvalidParams; + + CVideoDbUrl videoUrl; + if (!videoUrl.FromString("videodb://musicvideos/titles/")) + return InternalError; + + int genreID = -1, year = -1; + const CVariant &filter = parameterObject["filter"]; + if (filter.isMember("artist")) + videoUrl.AddOption("artist", filter["artist"].asString()); + else if (filter.isMember("genreid")) + genreID = (int)filter["genreid"].asInteger(); + else if (filter.isMember("genre")) + videoUrl.AddOption("genre", filter["genre"].asString()); + else if (filter.isMember("year")) + year = (int)filter["year"].asInteger(); + else if (filter.isMember("director")) + videoUrl.AddOption("director", filter["director"].asString()); + else if (filter.isMember("studio")) + videoUrl.AddOption("studio", filter["studio"].asString()); + else if (filter.isMember("tag")) + videoUrl.AddOption("tag", filter["tag"].asString()); + else if (filter.isObject()) + { + std::string xsp; + if (!GetXspFiltering("musicvideos", filter, xsp)) + return InvalidParams; + + videoUrl.AddOption("xsp", xsp); + } + + CFileItemList items; + if (!videodatabase.GetMusicVideosNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, -1, sorting, RequiresAdditionalDetails(MediaTypeMusicVideo, parameterObject))) + return InternalError; + + return HandleItems("musicvideoid", "musicvideos", items, parameterObject, result, false); +} + +JSONRPC_STATUS CVideoLibrary::GetMusicVideoDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + int id = (int)parameterObject["musicvideoid"].asInteger(); + + CVideoInfoTag infos; + if (!videodatabase.GetMusicVideoInfo("", infos, id, RequiresAdditionalDetails(MediaTypeMusicVideo, parameterObject)) || infos.m_iDbId <= 0) + return InvalidParams; + + HandleFileItem("musicvideoid", true, "musicvideodetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false); + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedMovies(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CFileItemList items; + if (!videodatabase.GetRecentlyAddedMoviesNav("videodb://recentlyaddedmovies/", items, 0, RequiresAdditionalDetails(MediaTypeMovie, parameterObject))) + return InternalError; + + return HandleItems("movieid", "movies", items, parameterObject, result, true); +} + +JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedEpisodes(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CFileItemList items; + if (!videodatabase.GetRecentlyAddedEpisodesNav("videodb://recentlyaddedepisodes/", items, 0, RequiresAdditionalDetails(MediaTypeEpisode, parameterObject))) + return InternalError; + + return HandleItems("episodeid", "episodes", items, parameterObject, result, true); +} + +JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedMusicVideos(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CFileItemList items; + if (!videodatabase.GetRecentlyAddedMusicVideosNav("videodb://recentlyaddedmusicvideos/", items, 0, RequiresAdditionalDetails(MediaTypeMusicVideo, parameterObject))) + return InternalError; + + return HandleItems("musicvideoid", "musicvideos", items, parameterObject, result, true); +} + +JSONRPC_STATUS CVideoLibrary::GetInProgressTVShows(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CFileItemList items; + if (!videodatabase.GetInProgressTvShowsNav("videodb://inprogresstvshows/", items, 0, RequiresAdditionalDetails(MediaTypeTvShow, parameterObject))) + return InternalError; + + return HandleItems("tvshowid", "tvshows", items, parameterObject, result, false); +} + +JSONRPC_STATUS CVideoLibrary::GetGenres(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::string media = parameterObject["type"].asString(); + StringUtils::ToLower(media); + VideoDbContentType idContent = VideoDbContentType::UNKNOWN; + + std::string strPath = "videodb://"; + /* select which video content to get genres from*/ + if (media == MediaTypeMovie) + { + idContent = VideoDbContentType::MOVIES; + strPath += "movies"; + } + else if (media == MediaTypeTvShow) + { + idContent = VideoDbContentType::TVSHOWS; + strPath += "tvshows"; + } + else if (media == MediaTypeMusicVideo) + { + idContent = VideoDbContentType::MUSICVIDEOS; + strPath += "musicvideos"; + } + strPath += "/genres/"; + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CFileItemList items; + if (!videodatabase.GetGenresNav(strPath, items, idContent)) + return InternalError; + + /* need to set strTitle in each item*/ + for (unsigned int i = 0; i < (unsigned int)items.Size(); i++) + items[i]->GetVideoInfoTag()->m_strTitle = items[i]->GetLabel(); + + HandleFileItemList("genreid", false, "genres", items, parameterObject, result); + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetTags(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::string media = parameterObject["type"].asString(); + StringUtils::ToLower(media); + VideoDbContentType idContent = VideoDbContentType::UNKNOWN; + + std::string strPath = "videodb://"; + /* select which video content to get tags from*/ + if (media == MediaTypeMovie) + { + idContent = VideoDbContentType::MOVIES; + strPath += "movies"; + } + else if (media == MediaTypeTvShow) + { + idContent = VideoDbContentType::TVSHOWS; + strPath += "tvshows"; + } + else if (media == MediaTypeMusicVideo) + { + idContent = VideoDbContentType::MUSICVIDEOS; + strPath += "musicvideos"; + } + strPath += "/tags/"; + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CFileItemList items; + if (!videodatabase.GetTagsNav(strPath, items, idContent)) + return InternalError; + + /* need to set strTitle in each item*/ + for (int i = 0; i < items.Size(); i++) + items[i]->GetVideoInfoTag()->m_strTitle = items[i]->GetLabel(); + + HandleFileItemList("tagid", false, "tags", items, parameterObject, result); + return OK; +} + +namespace +{ + const std::map<std::string, std::string> mediaIDTypes = { + {"episodeid", MediaTypeEpisode}, + {"tvshowid", MediaTypeTvShow}, + {"seasonid", MediaTypeSeason}, + {"movieid", MediaTypeMovie}, + {"setid", MediaTypeVideoCollection}, + {"musicvideoid", MediaTypeMusicVideo}, + }; +} + +JSONRPC_STATUS CVideoLibrary::GetAvailableArtTypes(const std::string& method, ITransportLayer* transport, IClient* client, const CVariant& parameterObject, CVariant& result) +{ + std::string mediaType; + int mediaID = -1; + for (const auto& mediaIDType : mediaIDTypes) { + if (parameterObject["item"].isMember(mediaIDType.first)) + { + mediaType = mediaIDType.second; + mediaID = parameterObject["item"][mediaIDType.first].asInteger32(); + break; + } + } + if (mediaID == -1) + return InternalError; + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVariant availablearttypes = CVariant(CVariant::VariantTypeArray); + for (const auto& artType : videodatabase.GetAvailableArtTypesForItem(mediaID, mediaType)) + { + availablearttypes.append(artType); + } + result = CVariant(CVariant::VariantTypeObject); + result["availablearttypes"] = availablearttypes; + + return OK; +} + +JSONRPC_STATUS CVideoLibrary::GetAvailableArt(const std::string& method, ITransportLayer* transport, IClient* client, const CVariant& parameterObject, CVariant& result) +{ + std::string mediaType; + int mediaID = -1; + for (const auto& mediaIDType : mediaIDTypes) { + if (parameterObject["item"].isMember(mediaIDType.first)) + { + mediaType = mediaIDType.second; + mediaID = parameterObject["item"][mediaIDType.first].asInteger32(); + break; + } + } + if (mediaID == -1) + return InternalError; + + std::string artType = parameterObject["arttype"].asString(); + StringUtils::ToLower(artType); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVariant availableart = CVariant(CVariant::VariantTypeArray); + for (const auto& artentry : videodatabase.GetAvailableArtForItem(mediaID, mediaType, artType)) + { + CVariant item = CVariant(CVariant::VariantTypeObject); + item["url"] = CTextureUtils::GetWrappedImageURL(artentry.m_url); + item["arttype"] = artentry.m_aspect; + if (!artentry.m_preview.empty()) + item["previewurl"] = CTextureUtils::GetWrappedImageURL(artentry.m_preview); + availableart.append(item); + } + result = CVariant(CVariant::VariantTypeObject); + result["availableart"] = availableart; + + return OK; +} + +JSONRPC_STATUS CVideoLibrary::SetMovieDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["movieid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + if (!videodatabase.GetMovieInfo("", infos, id) || infos.m_iDbId <= 0) + return InvalidParams; + + // get artwork + std::map<std::string, std::string> artwork; + videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork); + + int playcount = infos.GetPlayCount(); + CDateTime lastPlayed = infos.m_lastPlayed; + + std::set<std::string> removedArtwork; + std::set<std::string> updatedDetails; + UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails); + + if (videodatabase.UpdateDetailsForMovie(id, infos, artwork, updatedDetails) <= 0) + return InternalError; + + if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeMovie, removedArtwork)) + return InternalError; + + if (playcount != infos.GetPlayCount() || lastPlayed != infos.m_lastPlayed) + { + // restore original playcount or the new one won't be announced + int newPlaycount = infos.GetPlayCount(); + infos.SetPlayCount(playcount); + videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed); + } + + UpdateResumePoint(parameterObject, infos, videodatabase); + + CJSONRPCUtils::NotifyItemUpdated(infos, artwork); + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::SetMovieSetDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["setid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + videodatabase.GetSetInfo(id, infos); + if (infos.m_iDbId <= 0) + { + videodatabase.Close(); + return InvalidParams; + } + + // get artwork + std::map<std::string, std::string> artwork; + videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork); + + std::set<std::string> removedArtwork; + std::set<std::string> updatedDetails; + UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails); + + if (videodatabase.SetDetailsForMovieSet(infos, artwork, id) <= 0) + return InternalError; + + if (!videodatabase.RemoveArtForItem(infos.m_iDbId, "set", removedArtwork)) + return InternalError; + + CJSONRPCUtils::NotifyItemUpdated(); + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::SetTVShowDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["tvshowid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + if (!videodatabase.GetTvShowInfo("", infos, id) || infos.m_iDbId <= 0) + return InvalidParams; + + // get artwork + std::map<std::string, std::string> artwork; + videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork); + + std::map<int, std::map<std::string, std::string> > seasonArt; + videodatabase.GetTvShowSeasonArt(infos.m_iDbId, seasonArt); + + std::set<std::string> removedArtwork; + std::set<std::string> updatedDetails; + UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails); + + // we need to manually remove tags/taglinks for now because they aren't replaced + // due to scrapers not supporting them + videodatabase.RemoveTagsFromItem(id, MediaTypeTvShow); + + if (!videodatabase.UpdateDetailsForTvShow(id, infos, artwork, seasonArt)) + return InternalError; + + if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeTvShow, removedArtwork)) + return InternalError; + + CJSONRPCUtils::NotifyItemUpdated(); + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::SetSeasonDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["seasonid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + videodatabase.GetSeasonInfo(id, infos); + if (infos.m_iDbId <= 0 || infos.m_iIdShow <= 0) + { + videodatabase.Close(); + return InvalidParams; + } + + // get artwork + std::map<std::string, std::string> artwork; + videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork); + + std::set<std::string> removedArtwork; + std::set<std::string> updatedDetails; + UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails); + if (ParameterNotNull(parameterObject, "title")) + infos.SetSortTitle(parameterObject["title"].asString()); + + if (videodatabase.SetDetailsForSeason(infos, artwork, infos.m_iIdShow, id) <= 0) + return InternalError; + + if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeSeason, removedArtwork)) + return InternalError; + + CJSONRPCUtils::NotifyItemUpdated(); + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::SetEpisodeDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["episodeid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + videodatabase.GetEpisodeInfo("", infos, id); + if (infos.m_iDbId <= 0) + { + videodatabase.Close(); + return InvalidParams; + } + + int tvshowid = videodatabase.GetTvShowForEpisode(id); + if (tvshowid <= 0) + { + videodatabase.Close(); + return InvalidParams; + } + + // get artwork + std::map<std::string, std::string> artwork; + videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork); + + int playcount = infos.GetPlayCount(); + CDateTime lastPlayed = infos.m_lastPlayed; + + std::set<std::string> removedArtwork; + std::set<std::string> updatedDetails; + UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails); + + if (videodatabase.SetDetailsForEpisode(infos, artwork, tvshowid, id) <= 0) + return InternalError; + + if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeEpisode, removedArtwork)) + return InternalError; + + if (playcount != infos.GetPlayCount() || lastPlayed != infos.m_lastPlayed) + { + // restore original playcount or the new one won't be announced + int newPlaycount = infos.GetPlayCount(); + infos.SetPlayCount(playcount); + videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed); + } + + UpdateResumePoint(parameterObject, infos, videodatabase); + + CJSONRPCUtils::NotifyItemUpdated(); + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::SetMusicVideoDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["musicvideoid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + videodatabase.GetMusicVideoInfo("", infos, id); + if (infos.m_iDbId <= 0) + { + videodatabase.Close(); + return InvalidParams; + } + + // get artwork + std::map<std::string, std::string> artwork; + videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork); + + int playcount = infos.GetPlayCount(); + CDateTime lastPlayed = infos.m_lastPlayed; + + std::set<std::string> removedArtwork; + std::set<std::string> updatedDetails; + UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails); + + // we need to manually remove tags/taglinks for now because they aren't replaced + // due to scrapers not supporting them + videodatabase.RemoveTagsFromItem(id, MediaTypeMusicVideo); + + if (videodatabase.SetDetailsForMusicVideo(infos, artwork, id) <= 0) + return InternalError; + + if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeMusicVideo, removedArtwork)) + return InternalError; + + if (playcount != infos.GetPlayCount()|| lastPlayed != infos.m_lastPlayed) + { + // restore original playcount or the new one won't be announced + int newPlaycount = infos.GetPlayCount(); + infos.SetPlayCount(playcount); + videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed); + } + + UpdateResumePoint(parameterObject, infos, videodatabase); + + CJSONRPCUtils::NotifyItemUpdated(); + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::RefreshMovie(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = static_cast<int>(parameterObject["movieid"].asInteger()); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + if (!videodatabase.GetMovieInfo("", infos, id) || infos.m_iDbId <= 0) + return InvalidParams; + + bool ignoreNfo = parameterObject["ignorenfo"].asBoolean(); + std::string searchTitle = parameterObject["title"].asString(); + CVideoLibraryQueue::GetInstance().RefreshItem(CFileItemPtr(new CFileItem(infos)), ignoreNfo, true, false, searchTitle); + + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::RefreshTVShow(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = static_cast<int>(parameterObject["tvshowid"].asInteger()); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CFileItemPtr item(new CFileItem()); + CVideoInfoTag infos; + if (!videodatabase.GetTvShowInfo("", infos, id, item.get()) || infos.m_iDbId <= 0) + return InvalidParams; + + item->SetFromVideoInfoTag(infos); + + bool ignoreNfo = parameterObject["ignorenfo"].asBoolean(); + bool refreshEpisodes = parameterObject["refreshepisodes"].asBoolean(); + std::string searchTitle = parameterObject["title"].asString(); + CVideoLibraryQueue::GetInstance().RefreshItem(item, ignoreNfo, true, refreshEpisodes, searchTitle); + + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::RefreshEpisode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = (int)parameterObject["episodeid"].asInteger(); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + if (!videodatabase.GetEpisodeInfo("", infos, id) || infos.m_iDbId <= 0) + return InvalidParams; + + CFileItemPtr item = CFileItemPtr(new CFileItem(infos)); + // We need to set the correct base path to get the valid fanart + int tvshowid = infos.m_iIdShow; + if (tvshowid <= 0) + tvshowid = videodatabase.GetTvShowForEpisode(id); + + bool ignoreNfo = parameterObject["ignorenfo"].asBoolean(); + std::string searchTitle = parameterObject["title"].asString(); + CVideoLibraryQueue::GetInstance().RefreshItem(item, ignoreNfo, true, false, searchTitle); + + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::RefreshMusicVideo(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + int id = static_cast<int>(parameterObject["musicvideoid"].asInteger()); + + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + CVideoInfoTag infos; + if (!videodatabase.GetMusicVideoInfo("", infos, id) || infos.m_iDbId <= 0) + return InvalidParams; + + bool ignoreNfo = parameterObject["ignorenfo"].asBoolean(); + std::string searchTitle = parameterObject["title"].asString(); + CVideoLibraryQueue::GetInstance().RefreshItem(CFileItemPtr(new CFileItem(infos)), ignoreNfo, true, false, searchTitle); + + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::RemoveMovie(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + return RemoveVideo(parameterObject); +} + +JSONRPC_STATUS CVideoLibrary::RemoveTVShow(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + return RemoveVideo(parameterObject); +} + +JSONRPC_STATUS CVideoLibrary::RemoveEpisode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + return RemoveVideo(parameterObject); +} + +JSONRPC_STATUS CVideoLibrary::RemoveMusicVideo(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + return RemoveVideo(parameterObject); +} + +JSONRPC_STATUS CVideoLibrary::Scan(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::string directory = parameterObject["directory"].asString(); + std::string cmd = + StringUtils::Format("updatelibrary(video, {}, {})", StringUtils::Paramify(directory), + parameterObject["showdialogs"].asBoolean() ? "true" : "false"); + + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::Export(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::string cmd; + if (parameterObject["options"].isMember("path")) + cmd = StringUtils::Format("exportlibrary2(video, singlefile, {})", + StringUtils::Paramify(parameterObject["options"]["path"].asString())); + else + { + cmd = "exportlibrary2(video, separate, dummy"; + if (parameterObject["options"]["images"].isBoolean() && + parameterObject["options"]["images"].asBoolean() == true) + cmd += ", artwork"; + if (parameterObject["options"]["overwrite"].isBoolean() && + parameterObject["options"]["overwrite"].asBoolean() == true) + cmd += ", overwrite"; + if (parameterObject["options"]["actorthumbs"].isBoolean() && + parameterObject["options"]["actorthumbs"].asBoolean() == true) + cmd += ", actorthumbs"; + cmd += ")"; + } + + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); + return ACK; +} + +JSONRPC_STATUS CVideoLibrary::Clean(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant ¶meterObject, CVariant &result) +{ + std::string directory = parameterObject["directory"].asString(); + std::string cmd; + if (parameterObject["content"].empty()) + cmd = StringUtils::Format("cleanlibrary(video, {0}, {1})", + parameterObject["showdialogs"].asBoolean() ? "true" : "false", + StringUtils::Paramify(directory)); + else + cmd = StringUtils::Format("cleanlibrary({0}, {1}, {2})", parameterObject["content"].asString(), + parameterObject["showdialogs"].asBoolean() ? "true" : "false", + StringUtils::Paramify(directory)); + + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd); + return ACK; +} + +bool CVideoLibrary::FillFileItem( + const std::string& strFilename, + std::shared_ptr<CFileItem>& item, + const CVariant& parameterObject /* = CVariant(CVariant::VariantTypeArray) */) +{ + CVideoDatabase videodatabase; + if (strFilename.empty()) + return false; + + bool filled = false; + if (videodatabase.Open()) + { + CVideoInfoTag details; + if (videodatabase.LoadVideoInfo(strFilename, details)) + { + item->SetFromVideoInfoTag(details); + item->SetDynPath(strFilename); + filled = true; + } + } + + if (item->GetLabel().empty()) + { + item->SetLabel(CUtil::GetTitleFromPath(strFilename, false)); + if (item->GetLabel().empty()) + item->SetLabel(URIUtils::GetFileName(strFilename)); + } + + return filled; +} + +bool CVideoLibrary::FillFileItemList(const CVariant ¶meterObject, CFileItemList &list) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return false; + + std::string file = parameterObject["file"].asString(); + int movieID = (int)parameterObject["movieid"].asInteger(-1); + int episodeID = (int)parameterObject["episodeid"].asInteger(-1); + int musicVideoID = (int)parameterObject["musicvideoid"].asInteger(-1); + int recordingID = static_cast<int>(parameterObject["recordingid"].asInteger()); + + bool success = false; + CFileItemPtr fileItem(new CFileItem()); + if (FillFileItem(file, fileItem)) + { + success = true; + list.Add(fileItem); + } + + if (movieID > 0) + { + CVideoInfoTag details; + videodatabase.GetMovieInfo("", details, movieID); + if (!details.IsEmpty()) + { + list.Add(CFileItemPtr(new CFileItem(details))); + success = true; + } + } + if (episodeID > 0) + { + CVideoInfoTag details; + if (videodatabase.GetEpisodeInfo("", details, episodeID) && !details.IsEmpty()) + { + list.Add(CFileItemPtr(new CFileItem(details))); + success = true; + } + } + if (musicVideoID > 0) + { + CVideoInfoTag details; + videodatabase.GetMusicVideoInfo("", details, musicVideoID); + if (!details.IsEmpty()) + { + list.Add(CFileItemPtr(new CFileItem(details))); + success = true; + } + } + if (recordingID > 0) + { + std::shared_ptr<CFileItem> recordingFileItem = + CPVROperations::GetRecordingFileItem(recordingID); + + if (recordingFileItem) + { + list.Add(recordingFileItem); + success = true; + } + } + + return success; +} + +int CVideoLibrary::RequiresAdditionalDetails(const MediaType& mediaType, const CVariant ¶meterObject) +{ + if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo) + return VideoDbDetailsNone; + + return GetDetailsFromJsonParameters(parameterObject); +} + +int CVideoLibrary::GetDetailsFromJsonParameters(const CVariant& parameterObject) +{ + const CVariant& properties = parameterObject["properties"]; + int details = VideoDbDetailsNone; + for (CVariant::const_iterator_array itr = properties.begin_array(); itr != properties.end_array(); + ++itr) + { + std::string propertyValue = itr->asString(); + if (propertyValue == "cast") + details = details | VideoDbDetailsCast; + else if (propertyValue == "ratings") + details = details | VideoDbDetailsRating; + else if (propertyValue == "uniqueid") + details = details | VideoDbDetailsUniqueID; + else if (propertyValue == "showlink") + details = details | VideoDbDetailsShowLink; + else if (propertyValue == "streamdetails") + details = details | VideoDbDetailsStream; + else if (propertyValue == "tag") + details = details | VideoDbDetailsTag; + } + return details; +} + +JSONRPC_STATUS CVideoLibrary::HandleItems(const char *idProperty, const char *resultName, CFileItemList &items, const CVariant ¶meterObject, CVariant &result, bool limit /* = true */) +{ + int size = items.Size(); + if (!limit && items.HasProperty("total") && items.GetProperty("total").asInteger() > size) + size = (int)items.GetProperty("total").asInteger(); + HandleFileItemList(idProperty, true, resultName, items, parameterObject, result, size, limit); + + return OK; +} + +JSONRPC_STATUS CVideoLibrary::RemoveVideo(const CVariant ¶meterObject) +{ + CVideoDatabase videodatabase; + if (!videodatabase.Open()) + return InternalError; + + if (parameterObject.isMember("movieid")) + videodatabase.DeleteMovie((int)parameterObject["movieid"].asInteger()); + else if (parameterObject.isMember("tvshowid")) + videodatabase.DeleteTvShow((int)parameterObject["tvshowid"].asInteger()); + else if (parameterObject.isMember("episodeid")) + videodatabase.DeleteEpisode((int)parameterObject["episodeid"].asInteger()); + else if (parameterObject.isMember("musicvideoid")) + videodatabase.DeleteMusicVideo((int)parameterObject["musicvideoid"].asInteger()); + + CJSONRPCUtils::NotifyItemUpdated(); + return ACK; +} + +void CVideoLibrary::UpdateResumePoint(const CVariant ¶meterObject, CVideoInfoTag &details, CVideoDatabase &videodatabase) +{ + if (!parameterObject["resume"].isNull()) + { + double position = (double)parameterObject["resume"]["position"].asDouble(); + if (position == 0.0) + videodatabase.ClearBookMarksOfFile(details.m_strFileNameAndPath, CBookmark::RESUME); + else + { + CBookmark bookmark; + double total = (double)parameterObject["resume"]["total"].asDouble(); + if (total <= 0.0 && !videodatabase.GetResumeBookMark(details.m_strFileNameAndPath, bookmark)) + bookmark.totalTimeInSeconds = details.m_streamDetails.GetVideoDuration(); + else + bookmark.totalTimeInSeconds = total; + + bookmark.timeInSeconds = position; + videodatabase.AddBookMarkToFile(details.m_strFileNameAndPath, bookmark, CBookmark::RESUME); + } + } +} + +void CVideoLibrary::UpdateVideoTagField(const CVariant& parameterObject, const std::string& fieldName, std::vector<std::string>& fieldValue, std::set<std::string>& updatedDetails) +{ + if (ParameterNotNull(parameterObject, fieldName)) + { + CopyStringArray(parameterObject[fieldName], fieldValue); + updatedDetails.insert(fieldName); + } +} + +void CVideoLibrary::UpdateVideoTag(const CVariant ¶meterObject, CVideoInfoTag& details, std::map<std::string, std::string> &artwork, std::set<std::string> &removedArtwork, std::set<std::string> &updatedDetails) +{ + if (ParameterNotNull(parameterObject, "title")) + details.SetTitle(parameterObject["title"].asString()); + if (ParameterNotNull(parameterObject, "playcount")) + details.SetPlayCount(static_cast<int>(parameterObject["playcount"].asInteger())); + if (ParameterNotNull(parameterObject, "runtime")) + details.SetDuration(static_cast<int>(parameterObject["runtime"].asInteger())); + + std::vector<std::string> director(details.m_director); + UpdateVideoTagField(parameterObject, "director", director, updatedDetails); + details.SetDirector(director); + + std::vector<std::string> studio(details.m_studio); + UpdateVideoTagField(parameterObject, "studio", studio, updatedDetails); + details.SetStudio(studio); + + if (ParameterNotNull(parameterObject, "plot")) + details.SetPlot(parameterObject["plot"].asString()); + if (ParameterNotNull(parameterObject, "album")) + details.SetAlbum(parameterObject["album"].asString()); + + std::vector<std::string> artist(details.m_artist); + UpdateVideoTagField(parameterObject, "artist", artist, updatedDetails); + details.SetArtist(artist); + + std::vector<std::string> genre(details.m_genre); + UpdateVideoTagField(parameterObject, "genre", genre, updatedDetails); + details.SetGenre(genre); + + if (ParameterNotNull(parameterObject, "track")) + details.m_iTrack = (int)parameterObject["track"].asInteger(); + if (ParameterNotNull(parameterObject, "rating")) + { + details.SetRating(parameterObject["rating"].asFloat()); + updatedDetails.insert("ratings"); + } + if (ParameterNotNull(parameterObject, "votes")) + { + details.SetVotes(StringUtils::ReturnDigits(parameterObject["votes"].asString())); + updatedDetails.insert("ratings"); //Votes and ratings both need updates now, this will trigger those + } + if (ParameterNotNull(parameterObject, "ratings")) + { + CVariant ratings = parameterObject["ratings"]; + for (CVariant::const_iterator_map rIt = ratings.begin_map(); rIt != ratings.end_map(); ++rIt) + { + if (rIt->second.isObject() && ParameterNotNull(rIt->second, "rating")) + { + const auto& rating = rIt->second; + if (ParameterNotNull(rating, "votes")) + { + details.SetRating(rating["rating"].asFloat(), + static_cast<int>(rating["votes"].asInteger()), + rIt->first, + (ParameterNotNull(rating, "default") && rating["default"].asBoolean())); + } + else + details.SetRating(rating["rating"].asFloat(), rIt->first, (ParameterNotNull(rating, "default") && rating["default"].asBoolean())); + + updatedDetails.insert("ratings"); + } + else if (rIt->second.isNull()) + { + details.RemoveRating(rIt->first); + updatedDetails.insert("ratings"); + } + } + } + if (ParameterNotNull(parameterObject, "userrating")) + details.m_iUserRating = static_cast<int>(parameterObject["userrating"].asInteger()); + if (ParameterNotNull(parameterObject, "mpaa")) + details.SetMPAARating(parameterObject["mpaa"].asString()); + if (ParameterNotNull(parameterObject, "imdbnumber")) + { + details.SetUniqueID(parameterObject["imdbnumber"].asString()); + updatedDetails.insert("uniqueid"); + } + if (ParameterNotNull(parameterObject, "uniqueid")) + { + CVariant uniqueids = parameterObject["uniqueid"]; + for (CVariant::const_iterator_map idIt = uniqueids.begin_map(); idIt != uniqueids.end_map(); + ++idIt) + { + if (idIt->second.isString() && !idIt->second.asString().empty()) + { + details.SetUniqueID(idIt->second.asString(), idIt->first); + updatedDetails.insert("uniqueid"); + } + else if (idIt->second.isNull() && idIt->first != details.GetDefaultUniqueID()) + { + details.RemoveUniqueID(idIt->first); + updatedDetails.insert("uniqueid"); + } + } + } + if (ParameterNotNull(parameterObject, "premiered")) + { + CDateTime premiered; + SetFromDBDate(parameterObject["premiered"], premiered); + details.SetPremiered(premiered); + } + else if (ParameterNotNull(parameterObject, "year")) + details.SetYear((int)parameterObject["year"].asInteger()); + if (ParameterNotNull(parameterObject, "lastplayed")) + SetFromDBDateTime(parameterObject["lastplayed"], details.m_lastPlayed); + if (ParameterNotNull(parameterObject, "firstaired")) + SetFromDBDate(parameterObject["firstaired"], details.m_firstAired); + if (ParameterNotNull(parameterObject, "productioncode")) + details.SetProductionCode(parameterObject["productioncode"].asString()); + if (ParameterNotNull(parameterObject, "season")) + details.m_iSeason = (int)parameterObject["season"].asInteger(); + if (ParameterNotNull(parameterObject, "episode")) + details.m_iEpisode = (int)parameterObject["episode"].asInteger(); + if (ParameterNotNull(parameterObject, "originaltitle")) + details.SetOriginalTitle(parameterObject["originaltitle"].asString()); + if (ParameterNotNull(parameterObject, "trailer")) + details.SetTrailer(parameterObject["trailer"].asString()); + if (ParameterNotNull(parameterObject, "tagline")) + details.SetTagLine(parameterObject["tagline"].asString()); + if (ParameterNotNull(parameterObject, "status")) + details.SetStatus(parameterObject["status"].asString()); + if (ParameterNotNull(parameterObject, "plotoutline")) + details.SetPlotOutline(parameterObject["plotoutline"].asString()); + + std::vector<std::string> credits(details.m_writingCredits); + UpdateVideoTagField(parameterObject, "writer", credits, updatedDetails); + details.SetWritingCredits(credits); + + std::vector<std::string> country(details.m_country); + UpdateVideoTagField(parameterObject, "country", country, updatedDetails); + details.SetCountry(country); + + if (ParameterNotNull(parameterObject, "top250")) + details.m_iTop250 = (int)parameterObject["top250"].asInteger(); + if (ParameterNotNull(parameterObject, "sorttitle")) + details.SetSortTitle(parameterObject["sorttitle"].asString()); + if (ParameterNotNull(parameterObject, "episodeguide")) + details.SetEpisodeGuide(parameterObject["episodeguide"].asString()); + if (ParameterNotNull(parameterObject, "set")) + { + details.SetSet(parameterObject["set"].asString()); + updatedDetails.insert("set"); + } + + std::vector<std::string> showLink(details.m_showLink); + UpdateVideoTagField(parameterObject, "showlink", showLink, updatedDetails); + details.SetShowLink(showLink); + + std::vector<std::string> tags(details.m_tags); + UpdateVideoTagField(parameterObject, "tag", tags, updatedDetails); + details.SetTags(tags); + + if (ParameterNotNull(parameterObject, "thumbnail")) + { + std::string value = parameterObject["thumbnail"].asString(); + artwork["thumb"] = StringUtils::Trim(value); + updatedDetails.insert("art.altered"); + } + if (ParameterNotNull(parameterObject, "fanart")) + { + std::string value = parameterObject["fanart"].asString(); + artwork["fanart"] = StringUtils::Trim(value); + updatedDetails.insert("art.altered"); + } + + if (ParameterNotNull(parameterObject, "art")) + { + CVariant art = parameterObject["art"]; + for (CVariant::const_iterator_map artIt = art.begin_map(); artIt != art.end_map(); ++artIt) + { + if (artIt->second.isString() && !artIt->second.asString().empty()) + { + artwork[artIt->first] = CTextureUtils::UnwrapImageURL(artIt->second.asString()); + updatedDetails.insert("art.altered"); + } + else if (artIt->second.isNull()) + { + artwork.erase(artIt->first); + removedArtwork.insert(artIt->first); + } + } + } + + if (ParameterNotNull(parameterObject, "dateadded")) + { + SetFromDBDateTime(parameterObject["dateadded"], details.m_dateAdded); + updatedDetails.insert("dateadded"); + } +} |