1 /*
2  *  Copyright (C) 2016-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "VideoLibrary.h"
10 
11 #include "TextureDatabase.h"
12 #include "Util.h"
13 #include "messaging/ApplicationMessenger.h"
14 #include "utils/SortUtils.h"
15 #include "utils/StringUtils.h"
16 #include "utils/URIUtils.h"
17 #include "utils/Variant.h"
18 #include "video/VideoDatabase.h"
19 #include "video/VideoLibraryQueue.h"
20 
21 using namespace JSONRPC;
22 using namespace KODI::MESSAGING;
23 
GetMovies(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)24 JSONRPC_STATUS CVideoLibrary::GetMovies(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
25 {
26   CVideoDatabase videodatabase;
27   if (!videodatabase.Open())
28     return InternalError;
29 
30   SortDescription sorting;
31   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
32   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
33     return InvalidParams;
34 
35   CVideoDbUrl videoUrl;
36   if (!videoUrl.FromString("videodb://movies/titles/"))
37     return InternalError;
38 
39   int genreID = -1, year = -1, setID = 0;
40   const CVariant &filter = parameterObject["filter"];
41   if (filter.isMember("genreid"))
42     genreID = (int)filter["genreid"].asInteger();
43   else if (filter.isMember("genre"))
44     videoUrl.AddOption("genre", filter["genre"].asString());
45   else if (filter.isMember("year"))
46     year = (int)filter["year"].asInteger();
47   else if (filter.isMember("actor"))
48     videoUrl.AddOption("actor", filter["actor"].asString());
49   else if (filter.isMember("director"))
50     videoUrl.AddOption("director", filter["director"].asString());
51   else if (filter.isMember("studio"))
52     videoUrl.AddOption("studio", filter["studio"].asString());
53   else if (filter.isMember("country"))
54     videoUrl.AddOption("country", filter["country"].asString());
55   else if (filter.isMember("setid"))
56     setID = (int)filter["setid"].asInteger();
57   else if (filter.isMember("set"))
58     videoUrl.AddOption("set", filter["set"].asString());
59   else if (filter.isMember("tag"))
60     videoUrl.AddOption("tag", filter["tag"].asString());
61   else if (filter.isObject())
62   {
63     std::string xsp;
64     if (!GetXspFiltering("movies", filter, xsp))
65       return InvalidParams;
66 
67     videoUrl.AddOption("xsp", xsp);
68   }
69 
70   // setID must not be -1 otherwise GetMoviesNav() will return sets
71   if (setID < 0)
72     setID = 0;
73 
74   CFileItemList items;
75   if (!videodatabase.GetMoviesNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, setID, -1, sorting, RequiresAdditionalDetails(MediaTypeMovie, parameterObject)))
76     return InvalidParams;
77 
78   return HandleItems("movieid", "movies", items, parameterObject, result, false);
79 }
80 
GetMovieDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)81 JSONRPC_STATUS CVideoLibrary::GetMovieDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
82 {
83   int id = (int)parameterObject["movieid"].asInteger();
84 
85   CVideoDatabase videodatabase;
86   if (!videodatabase.Open())
87     return InternalError;
88 
89   CVideoInfoTag infos;
90   if (!videodatabase.GetMovieInfo("", infos, id, RequiresAdditionalDetails(MediaTypeMovie, parameterObject)) || infos.m_iDbId <= 0)
91     return InvalidParams;
92 
93   HandleFileItem("movieid", true, "moviedetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
94   return OK;
95 }
96 
GetMovieSets(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)97 JSONRPC_STATUS CVideoLibrary::GetMovieSets(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
98 {
99   CVideoDatabase videodatabase;
100   if (!videodatabase.Open())
101     return InternalError;
102 
103   CFileItemList items;
104   if (!videodatabase.GetSetsNav("videodb://movies/sets/", items, VIDEODB_CONTENT_MOVIES))
105     return InternalError;
106 
107   HandleFileItemList("setid", false, "sets", items, parameterObject, result);
108   return OK;
109 }
110 
GetMovieSetDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)111 JSONRPC_STATUS CVideoLibrary::GetMovieSetDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
112 {
113   int id = (int)parameterObject["setid"].asInteger();
114 
115   CVideoDatabase videodatabase;
116   if (!videodatabase.Open())
117     return InternalError;
118 
119   // Get movie set details
120   CVideoInfoTag infos;
121   if (!videodatabase.GetSetInfo(id, infos) || infos.m_iDbId <= 0)
122     return InvalidParams;
123 
124   HandleFileItem("setid", false, "setdetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
125 
126   // Get movies from the set
127   CFileItemList items;
128   if (!videodatabase.GetMoviesNav("videodb://movies/titles/", items, -1, -1, -1, -1, -1, -1, id, -1, SortDescription(), RequiresAdditionalDetails(MediaTypeMovie, parameterObject["movies"])))
129     return InternalError;
130 
131   return HandleItems("movieid", "movies", items, parameterObject["movies"], result["setdetails"], true);
132 }
133 
GetTVShows(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)134 JSONRPC_STATUS CVideoLibrary::GetTVShows(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
135 {
136   CVideoDatabase videodatabase;
137   if (!videodatabase.Open())
138     return InternalError;
139 
140   SortDescription sorting;
141   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
142   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
143     return InvalidParams;
144 
145   CVideoDbUrl videoUrl;
146   if (!videoUrl.FromString("videodb://tvshows/titles/"))
147     return InternalError;
148 
149   const CVariant &filter = parameterObject["filter"];
150   if (filter.isMember("genreid"))
151     videoUrl.AddOption("genreid", (int)filter["genreid"].asInteger());
152   else if (filter.isMember("genre"))
153     videoUrl.AddOption("genre", filter["genre"].asString());
154   else if (filter.isMember("year"))
155     videoUrl.AddOption("year", (int)filter["year"].asInteger());
156   else if (filter.isMember("actor"))
157     videoUrl.AddOption("actor", filter["actor"].asString());
158   else if (filter.isMember("studio"))
159     videoUrl.AddOption("studio", filter["studio"].asString());
160   else if (filter.isMember("tag"))
161     videoUrl.AddOption("tag", filter["tag"].asString());
162   else if (filter.isObject())
163   {
164     std::string xsp;
165     if (!GetXspFiltering("tvshows", filter, xsp))
166       return InvalidParams;
167 
168     videoUrl.AddOption("xsp", xsp);
169   }
170 
171   CFileItemList items;
172   CDatabase::Filter nofilter;
173   if (!videodatabase.GetTvShowsByWhere(videoUrl.ToString(), nofilter, items, sorting, RequiresAdditionalDetails(MediaTypeTvShow, parameterObject)))
174     return InvalidParams;
175 
176   return HandleItems("tvshowid", "tvshows", items, parameterObject, result, false);
177 }
178 
GetTVShowDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)179 JSONRPC_STATUS CVideoLibrary::GetTVShowDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
180 {
181   CVideoDatabase videodatabase;
182   if (!videodatabase.Open())
183     return InternalError;
184 
185   int id = (int)parameterObject["tvshowid"].asInteger();
186 
187   CFileItemPtr fileItem(new CFileItem());
188   CVideoInfoTag infos;
189   if (!videodatabase.GetTvShowInfo("", infos, id, fileItem.get(), RequiresAdditionalDetails(MediaTypeTvShow, parameterObject)) || infos.m_iDbId <= 0)
190     return InvalidParams;
191 
192   fileItem->SetFromVideoInfoTag(infos);
193   HandleFileItem("tvshowid", true, "tvshowdetails", fileItem, parameterObject, parameterObject["properties"], result, false);
194   return OK;
195 }
196 
GetSeasons(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)197 JSONRPC_STATUS CVideoLibrary::GetSeasons(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
198 {
199   CVideoDatabase videodatabase;
200   if (!videodatabase.Open())
201     return InternalError;
202 
203   int tvshowID = (int)parameterObject["tvshowid"].asInteger();
204 
205   std::string strPath = StringUtils::Format("videodb://tvshows/titles/%i/", tvshowID);
206   CFileItemList items;
207   if (!videodatabase.GetSeasonsNav(strPath, items, -1, -1, -1, -1, tvshowID, false))
208     return InternalError;
209 
210   HandleFileItemList("seasonid", false, "seasons", items, parameterObject, result);
211   return OK;
212 }
213 
GetSeasonDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)214 JSONRPC_STATUS CVideoLibrary::GetSeasonDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
215 {
216   CVideoDatabase videodatabase;
217   if (!videodatabase.Open())
218     return InternalError;
219 
220   int id = (int)parameterObject["seasonid"].asInteger();
221 
222   CVideoInfoTag infos;
223   if (!videodatabase.GetSeasonInfo(id, infos) ||
224       infos.m_iDbId <= 0 || infos.m_iIdShow <= 0)
225     return InvalidParams;
226 
227   CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos));
228   HandleFileItem("seasonid", false, "seasondetails", pItem, parameterObject, parameterObject["properties"], result, false);
229   return OK;
230 }
231 
GetEpisodes(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)232 JSONRPC_STATUS CVideoLibrary::GetEpisodes(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
233 {
234   CVideoDatabase videodatabase;
235   if (!videodatabase.Open())
236     return InternalError;
237 
238   SortDescription sorting;
239   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
240   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
241     return InvalidParams;
242 
243   int tvshowID = (int)parameterObject["tvshowid"].asInteger();
244   int season   = (int)parameterObject["season"].asInteger();
245 
246   std::string strPath = StringUtils::Format("videodb://tvshows/titles/%i/%i/", tvshowID, season);
247 
248   CVideoDbUrl videoUrl;
249   if (!videoUrl.FromString(strPath))
250     return InternalError;
251 
252   const CVariant &filter = parameterObject["filter"];
253   if (filter.isMember("genreid"))
254     videoUrl.AddOption("genreid", (int)filter["genreid"].asInteger());
255   else if (filter.isMember("genre"))
256     videoUrl.AddOption("genre", filter["genre"].asString());
257   else if (filter.isMember("year"))
258     videoUrl.AddOption("year", (int)filter["year"].asInteger());
259   else if (filter.isMember("actor"))
260     videoUrl.AddOption("actor", filter["actor"].asString());
261   else if (filter.isMember("director"))
262     videoUrl.AddOption("director", filter["director"].asString());
263   else if (filter.isObject())
264   {
265     std::string xsp;
266     if (!GetXspFiltering("episodes", filter, xsp))
267       return InvalidParams;
268 
269     videoUrl.AddOption("xsp", xsp);
270   }
271 
272   if (tvshowID <= 0 && (season > 0 || videoUrl.HasOption("genreid") || videoUrl.HasOption("genre") || videoUrl.HasOption("actor")))
273     return InvalidParams;
274 
275   if (tvshowID > 0)
276   {
277     videoUrl.AddOption("tvshowid", tvshowID);
278     if (season >= 0)
279       videoUrl.AddOption("season", season);
280   }
281 
282   CFileItemList items;
283   if (!videodatabase.GetEpisodesByWhere(videoUrl.ToString(), CDatabase::Filter(), items, false, sorting, RequiresAdditionalDetails(MediaTypeEpisode, parameterObject)))
284     return InvalidParams;
285 
286   return HandleItems("episodeid", "episodes", items, parameterObject, result, false);
287 }
288 
GetEpisodeDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)289 JSONRPC_STATUS CVideoLibrary::GetEpisodeDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
290 {
291   CVideoDatabase videodatabase;
292   if (!videodatabase.Open())
293     return InternalError;
294 
295   int id = (int)parameterObject["episodeid"].asInteger();
296 
297   CVideoInfoTag infos;
298   if (!videodatabase.GetEpisodeInfo("", infos, id, RequiresAdditionalDetails(MediaTypeEpisode, parameterObject)) || infos.m_iDbId <= 0)
299     return InvalidParams;
300 
301   CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos));
302   // We need to set the correct base path to get the valid fanart
303   int tvshowid = infos.m_iIdShow;
304   if (tvshowid <= 0)
305     tvshowid = videodatabase.GetTvShowForEpisode(id);
306 
307   std::string basePath = StringUtils::Format("videodb://tvshows/titles/%i/%i/%i", tvshowid, infos.m_iSeason, id);
308   pItem->SetPath(basePath);
309 
310   HandleFileItem("episodeid", true, "episodedetails", pItem, parameterObject, parameterObject["properties"], result, false);
311   return OK;
312 }
313 
GetMusicVideos(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)314 JSONRPC_STATUS CVideoLibrary::GetMusicVideos(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
315 {
316   CVideoDatabase videodatabase;
317   if (!videodatabase.Open())
318     return InternalError;
319 
320   SortDescription sorting;
321   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
322   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
323     return InvalidParams;
324 
325   CVideoDbUrl videoUrl;
326   if (!videoUrl.FromString("videodb://musicvideos/titles/"))
327     return InternalError;
328 
329   int genreID = -1, year = -1;
330   const CVariant &filter = parameterObject["filter"];
331   if (filter.isMember("artist"))
332     videoUrl.AddOption("artist", filter["artist"].asString());
333   else if (filter.isMember("genreid"))
334     genreID = (int)filter["genreid"].asInteger();
335   else if (filter.isMember("genre"))
336     videoUrl.AddOption("genre", filter["genre"].asString());
337   else if (filter.isMember("year"))
338     year = (int)filter["year"].asInteger();
339   else if (filter.isMember("director"))
340     videoUrl.AddOption("director", filter["director"].asString());
341   else if (filter.isMember("studio"))
342     videoUrl.AddOption("studio", filter["studio"].asString());
343   else if (filter.isMember("tag"))
344     videoUrl.AddOption("tag", filter["tag"].asString());
345   else if (filter.isObject())
346   {
347     std::string xsp;
348     if (!GetXspFiltering("musicvideos", filter, xsp))
349       return InvalidParams;
350 
351     videoUrl.AddOption("xsp", xsp);
352   }
353 
354   CFileItemList items;
355   if (!videodatabase.GetMusicVideosNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, -1, sorting, RequiresAdditionalDetails(MediaTypeMusicVideo, parameterObject)))
356     return InternalError;
357 
358   return HandleItems("musicvideoid", "musicvideos", items, parameterObject, result, false);
359 }
360 
GetMusicVideoDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)361 JSONRPC_STATUS CVideoLibrary::GetMusicVideoDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
362 {
363   CVideoDatabase videodatabase;
364   if (!videodatabase.Open())
365     return InternalError;
366 
367   int id = (int)parameterObject["musicvideoid"].asInteger();
368 
369   CVideoInfoTag infos;
370   if (!videodatabase.GetMusicVideoInfo("", infos, id, RequiresAdditionalDetails(MediaTypeMusicVideo, parameterObject)) || infos.m_iDbId <= 0)
371     return InvalidParams;
372 
373   HandleFileItem("musicvideoid", true, "musicvideodetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
374   return OK;
375 }
376 
GetRecentlyAddedMovies(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)377 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedMovies(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
378 {
379   CVideoDatabase videodatabase;
380   if (!videodatabase.Open())
381     return InternalError;
382 
383   CFileItemList items;
384   if (!videodatabase.GetRecentlyAddedMoviesNav("videodb://recentlyaddedmovies/", items, 0, RequiresAdditionalDetails(MediaTypeMovie, parameterObject)))
385     return InternalError;
386 
387   return HandleItems("movieid", "movies", items, parameterObject, result, true);
388 }
389 
GetRecentlyAddedEpisodes(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)390 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedEpisodes(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
391 {
392   CVideoDatabase videodatabase;
393   if (!videodatabase.Open())
394     return InternalError;
395 
396   CFileItemList items;
397   if (!videodatabase.GetRecentlyAddedEpisodesNav("videodb://recentlyaddedepisodes/", items, 0, RequiresAdditionalDetails(MediaTypeEpisode, parameterObject)))
398     return InternalError;
399 
400   return HandleItems("episodeid", "episodes", items, parameterObject, result, true);
401 }
402 
GetRecentlyAddedMusicVideos(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)403 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedMusicVideos(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
404 {
405   CVideoDatabase videodatabase;
406   if (!videodatabase.Open())
407     return InternalError;
408 
409   CFileItemList items;
410   if (!videodatabase.GetRecentlyAddedMusicVideosNav("videodb://recentlyaddedmusicvideos/", items, 0, RequiresAdditionalDetails(MediaTypeMusicVideo, parameterObject)))
411     return InternalError;
412 
413   return HandleItems("musicvideoid", "musicvideos", items, parameterObject, result, true);
414 }
415 
GetInProgressTVShows(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)416 JSONRPC_STATUS CVideoLibrary::GetInProgressTVShows(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
417 {
418   CVideoDatabase videodatabase;
419   if (!videodatabase.Open())
420     return InternalError;
421 
422   CFileItemList items;
423   if (!videodatabase.GetInProgressTvShowsNav("videodb://inprogresstvshows/", items, 0, RequiresAdditionalDetails(MediaTypeTvShow, parameterObject)))
424     return InternalError;
425 
426   return HandleItems("tvshowid", "tvshows", items, parameterObject, result, false);
427 }
428 
GetGenres(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)429 JSONRPC_STATUS CVideoLibrary::GetGenres(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
430 {
431   std::string media = parameterObject["type"].asString();
432   StringUtils::ToLower(media);
433   int idContent = -1;
434 
435   std::string strPath = "videodb://";
436   /* select which video content to get genres from*/
437   if (media == MediaTypeMovie)
438   {
439     idContent = VIDEODB_CONTENT_MOVIES;
440     strPath += "movies";
441   }
442   else if (media == MediaTypeTvShow)
443   {
444     idContent = VIDEODB_CONTENT_TVSHOWS;
445     strPath += "tvshows";
446   }
447   else if (media == MediaTypeMusicVideo)
448   {
449     idContent = VIDEODB_CONTENT_MUSICVIDEOS;
450     strPath += "musicvideos";
451   }
452   strPath += "/genres/";
453 
454   CVideoDatabase videodatabase;
455   if (!videodatabase.Open())
456     return InternalError;
457 
458   CFileItemList items;
459   if (!videodatabase.GetGenresNav(strPath, items, idContent))
460     return InternalError;
461 
462   /* need to set strTitle in each item*/
463   for (unsigned int i = 0; i < (unsigned int)items.Size(); i++)
464     items[i]->GetVideoInfoTag()->m_strTitle = items[i]->GetLabel();
465 
466   HandleFileItemList("genreid", false, "genres", items, parameterObject, result);
467   return OK;
468 }
469 
GetTags(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)470 JSONRPC_STATUS CVideoLibrary::GetTags(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
471 {
472   std::string media = parameterObject["type"].asString();
473   StringUtils::ToLower(media);
474   int idContent = -1;
475 
476   std::string strPath = "videodb://";
477   /* select which video content to get tags from*/
478   if (media == MediaTypeMovie)
479   {
480     idContent = VIDEODB_CONTENT_MOVIES;
481     strPath += "movies";
482   }
483   else if (media == MediaTypeTvShow)
484   {
485     idContent = VIDEODB_CONTENT_TVSHOWS;
486     strPath += "tvshows";
487   }
488   else if (media == MediaTypeMusicVideo)
489   {
490     idContent = VIDEODB_CONTENT_MUSICVIDEOS;
491     strPath += "musicvideos";
492   }
493   strPath += "/tags/";
494 
495   CVideoDatabase videodatabase;
496   if (!videodatabase.Open())
497     return InternalError;
498 
499   CFileItemList items;
500   if (!videodatabase.GetTagsNav(strPath, items, idContent))
501     return InternalError;
502 
503   /* need to set strTitle in each item*/
504   for (int i = 0; i < items.Size(); i++)
505     items[i]->GetVideoInfoTag()->m_strTitle = items[i]->GetLabel();
506 
507   HandleFileItemList("tagid", false, "tags", items, parameterObject, result);
508   return OK;
509 }
510 
511 namespace
512 {
513   const std::map<std::string, std::string> mediaIDTypes = {
514     {"episodeid", MediaTypeEpisode},
515     {"tvshowid", MediaTypeTvShow},
516     {"seasonid", MediaTypeSeason},
517     {"movieid", MediaTypeMovie},
518     {"setid", MediaTypeVideoCollection},
519     {"musicvideoid", MediaTypeMusicVideo},
520   };
521 }
522 
GetAvailableArtTypes(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)523 JSONRPC_STATUS CVideoLibrary::GetAvailableArtTypes(const std::string& method, ITransportLayer* transport, IClient* client, const CVariant& parameterObject, CVariant& result)
524 {
525   std::string mediaType;
526   int mediaID = -1;
527   for (const auto& mediaIDType : mediaIDTypes) {
528     if (parameterObject["item"].isMember(mediaIDType.first))
529     {
530       mediaType = mediaIDType.second;
531       mediaID = parameterObject["item"][mediaIDType.first].asInteger32();
532       break;
533     }
534   }
535   if (mediaID == -1)
536     return InternalError;
537 
538   CVideoDatabase videodatabase;
539   if (!videodatabase.Open())
540     return InternalError;
541 
542   CVariant availablearttypes = CVariant(CVariant::VariantTypeArray);
543   for (const auto& artType : videodatabase.GetAvailableArtTypesForItem(mediaID, mediaType))
544   {
545     availablearttypes.append(artType);
546   }
547   result = CVariant(CVariant::VariantTypeObject);
548   result["availablearttypes"] = availablearttypes;
549 
550   return OK;
551 }
552 
GetAvailableArt(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)553 JSONRPC_STATUS CVideoLibrary::GetAvailableArt(const std::string& method, ITransportLayer* transport, IClient* client, const CVariant& parameterObject, CVariant& result)
554 {
555   std::string mediaType;
556   int mediaID = -1;
557   for (const auto& mediaIDType : mediaIDTypes) {
558     if (parameterObject["item"].isMember(mediaIDType.first))
559     {
560       mediaType = mediaIDType.second;
561       mediaID = parameterObject["item"][mediaIDType.first].asInteger32();
562       break;
563     }
564   }
565   if (mediaID == -1)
566     return InternalError;
567 
568   std::string artType = parameterObject["arttype"].asString();
569   StringUtils::ToLower(artType);
570 
571   CVideoDatabase videodatabase;
572   if (!videodatabase.Open())
573     return InternalError;
574 
575   CVariant availableart = CVariant(CVariant::VariantTypeArray);
576   for (const auto& artentry : videodatabase.GetAvailableArtForItem(mediaID, mediaType, artType))
577   {
578     CVariant item = CVariant(CVariant::VariantTypeObject);
579     item["url"] = CTextureUtils::GetWrappedImageURL(artentry.m_url);
580     item["arttype"] = artentry.m_aspect;
581     if (!artentry.m_preview.empty())
582       item["previewurl"] = CTextureUtils::GetWrappedImageURL(artentry.m_preview);
583     availableart.append(item);
584   }
585   result = CVariant(CVariant::VariantTypeObject);
586   result["availableart"] = availableart;
587 
588   return OK;
589 }
590 
SetMovieDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)591 JSONRPC_STATUS CVideoLibrary::SetMovieDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
592 {
593   int id = (int)parameterObject["movieid"].asInteger();
594 
595   CVideoDatabase videodatabase;
596   if (!videodatabase.Open())
597     return InternalError;
598 
599   CVideoInfoTag infos;
600   if (!videodatabase.GetMovieInfo("", infos, id) || infos.m_iDbId <= 0)
601     return InvalidParams;
602 
603   // get artwork
604   std::map<std::string, std::string> artwork;
605   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
606 
607   int playcount = infos.GetPlayCount();
608   CDateTime lastPlayed = infos.m_lastPlayed;
609 
610   std::set<std::string> removedArtwork;
611   std::set<std::string> updatedDetails;
612   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails);
613 
614   if (videodatabase.UpdateDetailsForMovie(id, infos, artwork, updatedDetails) <= 0)
615     return InternalError;
616 
617   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeMovie, removedArtwork))
618     return InternalError;
619 
620   if (playcount != infos.GetPlayCount() || lastPlayed != infos.m_lastPlayed)
621   {
622     // restore original playcount or the new one won't be announced
623     int newPlaycount = infos.GetPlayCount();
624     infos.SetPlayCount(playcount);
625     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed);
626   }
627 
628   UpdateResumePoint(parameterObject, infos, videodatabase);
629 
630   CJSONRPCUtils::NotifyItemUpdated(infos, artwork);
631   return ACK;
632 }
633 
SetMovieSetDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)634 JSONRPC_STATUS CVideoLibrary::SetMovieSetDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
635 {
636   int id = (int)parameterObject["setid"].asInteger();
637 
638   CVideoDatabase videodatabase;
639   if (!videodatabase.Open())
640     return InternalError;
641 
642   CVideoInfoTag infos;
643   videodatabase.GetSetInfo(id, infos);
644   if (infos.m_iDbId <= 0)
645   {
646     videodatabase.Close();
647     return InvalidParams;
648   }
649 
650   // get artwork
651   std::map<std::string, std::string> artwork;
652   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
653 
654   std::set<std::string> removedArtwork;
655   std::set<std::string> updatedDetails;
656   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails);
657 
658   if (videodatabase.SetDetailsForMovieSet(infos, artwork, id) <= 0)
659     return InternalError;
660 
661   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, "set", removedArtwork))
662     return InternalError;
663 
664   CJSONRPCUtils::NotifyItemUpdated();
665   return ACK;
666 }
667 
SetTVShowDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)668 JSONRPC_STATUS CVideoLibrary::SetTVShowDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
669 {
670   int id = (int)parameterObject["tvshowid"].asInteger();
671 
672   CVideoDatabase videodatabase;
673   if (!videodatabase.Open())
674     return InternalError;
675 
676   CVideoInfoTag infos;
677   if (!videodatabase.GetTvShowInfo("", infos, id) || infos.m_iDbId <= 0)
678     return InvalidParams;
679 
680   // get artwork
681   std::map<std::string, std::string> artwork;
682   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
683 
684   std::map<int, std::map<std::string, std::string> > seasonArt;
685   videodatabase.GetTvShowSeasonArt(infos.m_iDbId, seasonArt);
686 
687   std::set<std::string> removedArtwork;
688   std::set<std::string> updatedDetails;
689   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails);
690 
691   // we need to manually remove tags/taglinks for now because they aren't replaced
692   // due to scrapers not supporting them
693   videodatabase.RemoveTagsFromItem(id, MediaTypeTvShow);
694 
695   if (!videodatabase.UpdateDetailsForTvShow(id, infos, artwork, seasonArt))
696     return InternalError;
697 
698   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeTvShow, removedArtwork))
699     return InternalError;
700 
701   CJSONRPCUtils::NotifyItemUpdated();
702   return ACK;
703 }
704 
SetSeasonDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)705 JSONRPC_STATUS CVideoLibrary::SetSeasonDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
706 {
707   int id = (int)parameterObject["seasonid"].asInteger();
708 
709   CVideoDatabase videodatabase;
710   if (!videodatabase.Open())
711     return InternalError;
712 
713   CVideoInfoTag infos;
714   videodatabase.GetSeasonInfo(id, infos);
715   if (infos.m_iDbId <= 0 || infos.m_iIdShow <= 0)
716   {
717     videodatabase.Close();
718     return InvalidParams;
719   }
720 
721   // get artwork
722   std::map<std::string, std::string> artwork;
723   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
724 
725   std::set<std::string> removedArtwork;
726   std::set<std::string> updatedDetails;
727   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails);
728   if (ParameterNotNull(parameterObject, "title"))
729     infos.SetSortTitle(parameterObject["title"].asString());
730 
731   if (videodatabase.SetDetailsForSeason(infos, artwork, infos.m_iIdShow, id) <= 0)
732     return InternalError;
733 
734   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeSeason, removedArtwork))
735     return InternalError;
736 
737   CJSONRPCUtils::NotifyItemUpdated();
738   return ACK;
739 }
740 
SetEpisodeDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)741 JSONRPC_STATUS CVideoLibrary::SetEpisodeDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
742 {
743   int id = (int)parameterObject["episodeid"].asInteger();
744 
745   CVideoDatabase videodatabase;
746   if (!videodatabase.Open())
747     return InternalError;
748 
749   CVideoInfoTag infos;
750   videodatabase.GetEpisodeInfo("", infos, id);
751   if (infos.m_iDbId <= 0)
752   {
753     videodatabase.Close();
754     return InvalidParams;
755   }
756 
757   int tvshowid = videodatabase.GetTvShowForEpisode(id);
758   if (tvshowid <= 0)
759   {
760     videodatabase.Close();
761     return InvalidParams;
762   }
763 
764   // get artwork
765   std::map<std::string, std::string> artwork;
766   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
767 
768   int playcount = infos.GetPlayCount();
769   CDateTime lastPlayed = infos.m_lastPlayed;
770 
771   std::set<std::string> removedArtwork;
772   std::set<std::string> updatedDetails;
773   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails);
774 
775   if (videodatabase.SetDetailsForEpisode(infos.m_strFileNameAndPath, infos, artwork, tvshowid, id) <= 0)
776     return InternalError;
777 
778   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeEpisode, removedArtwork))
779     return InternalError;
780 
781   if (playcount != infos.GetPlayCount() || lastPlayed != infos.m_lastPlayed)
782   {
783     // restore original playcount or the new one won't be announced
784     int newPlaycount = infos.GetPlayCount();
785     infos.SetPlayCount(playcount);
786     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed);
787   }
788 
789   UpdateResumePoint(parameterObject, infos, videodatabase);
790 
791   CJSONRPCUtils::NotifyItemUpdated();
792   return ACK;
793 }
794 
SetMusicVideoDetails(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)795 JSONRPC_STATUS CVideoLibrary::SetMusicVideoDetails(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
796 {
797   int id = (int)parameterObject["musicvideoid"].asInteger();
798 
799   CVideoDatabase videodatabase;
800   if (!videodatabase.Open())
801     return InternalError;
802 
803   CVideoInfoTag infos;
804   videodatabase.GetMusicVideoInfo("", infos, id);
805   if (infos.m_iDbId <= 0)
806   {
807     videodatabase.Close();
808     return InvalidParams;
809   }
810 
811   // get artwork
812   std::map<std::string, std::string> artwork;
813   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
814 
815   int playcount = infos.GetPlayCount();
816   CDateTime lastPlayed = infos.m_lastPlayed;
817 
818   std::set<std::string> removedArtwork;
819   std::set<std::string> updatedDetails;
820   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork, updatedDetails);
821 
822   // we need to manually remove tags/taglinks for now because they aren't replaced
823   // due to scrapers not supporting them
824   videodatabase.RemoveTagsFromItem(id, MediaTypeMusicVideo);
825 
826   if (videodatabase.SetDetailsForMusicVideo(infos.m_strFileNameAndPath, infos, artwork, id) <= 0)
827     return InternalError;
828 
829   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, MediaTypeMusicVideo, removedArtwork))
830     return InternalError;
831 
832   if (playcount != infos.GetPlayCount()|| lastPlayed != infos.m_lastPlayed)
833   {
834     // restore original playcount or the new one won't be announced
835     int newPlaycount = infos.GetPlayCount();
836     infos.SetPlayCount(playcount);
837     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed);
838   }
839 
840   UpdateResumePoint(parameterObject, infos, videodatabase);
841 
842   CJSONRPCUtils::NotifyItemUpdated();
843   return ACK;
844 }
845 
RefreshMovie(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)846 JSONRPC_STATUS CVideoLibrary::RefreshMovie(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
847 {
848   int id = static_cast<int>(parameterObject["movieid"].asInteger());
849 
850   CVideoDatabase videodatabase;
851   if (!videodatabase.Open())
852     return InternalError;
853 
854   CVideoInfoTag infos;
855   if (!videodatabase.GetMovieInfo("", infos, id) || infos.m_iDbId <= 0)
856     return InvalidParams;
857 
858   bool ignoreNfo = parameterObject["ignorenfo"].asBoolean();
859   std::string searchTitle = parameterObject["title"].asString();
860   CVideoLibraryQueue::GetInstance().RefreshItem(CFileItemPtr(new CFileItem(infos)), ignoreNfo, true, false, searchTitle);
861 
862   return ACK;
863 }
864 
RefreshTVShow(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)865 JSONRPC_STATUS CVideoLibrary::RefreshTVShow(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
866 {
867   int id = static_cast<int>(parameterObject["tvshowid"].asInteger());
868 
869   CVideoDatabase videodatabase;
870   if (!videodatabase.Open())
871     return InternalError;
872 
873   CFileItemPtr item(new CFileItem());
874   CVideoInfoTag infos;
875   if (!videodatabase.GetTvShowInfo("", infos, id, item.get()) || infos.m_iDbId <= 0)
876     return InvalidParams;
877 
878   item->SetFromVideoInfoTag(infos);
879 
880   bool ignoreNfo = parameterObject["ignorenfo"].asBoolean();
881   bool refreshEpisodes = parameterObject["refreshepisodes"].asBoolean();
882   std::string searchTitle = parameterObject["title"].asString();
883   CVideoLibraryQueue::GetInstance().RefreshItem(item, ignoreNfo, true, refreshEpisodes, searchTitle);
884 
885   return ACK;
886 }
887 
RefreshEpisode(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)888 JSONRPC_STATUS CVideoLibrary::RefreshEpisode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
889 {
890   int id = (int)parameterObject["episodeid"].asInteger();
891 
892   CVideoDatabase videodatabase;
893   if (!videodatabase.Open())
894     return InternalError;
895 
896   CVideoInfoTag infos;
897   if (!videodatabase.GetEpisodeInfo("", infos, id) || infos.m_iDbId <= 0)
898     return InvalidParams;
899 
900   CFileItemPtr item = CFileItemPtr(new CFileItem(infos));
901   // We need to set the correct base path to get the valid fanart
902   int tvshowid = infos.m_iIdShow;
903   if (tvshowid <= 0)
904     tvshowid = videodatabase.GetTvShowForEpisode(id);
905 
906   bool ignoreNfo = parameterObject["ignorenfo"].asBoolean();
907   std::string searchTitle = parameterObject["title"].asString();
908   CVideoLibraryQueue::GetInstance().RefreshItem(item, ignoreNfo, true, false, searchTitle);
909 
910   return ACK;
911 }
912 
RefreshMusicVideo(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)913 JSONRPC_STATUS CVideoLibrary::RefreshMusicVideo(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
914 {
915   int id = static_cast<int>(parameterObject["musicvideoid"].asInteger());
916 
917   CVideoDatabase videodatabase;
918   if (!videodatabase.Open())
919     return InternalError;
920 
921   CVideoInfoTag infos;
922   if (!videodatabase.GetMusicVideoInfo("", infos, id) || infos.m_iDbId <= 0)
923     return InvalidParams;
924 
925   bool ignoreNfo = parameterObject["ignorenfo"].asBoolean();
926   std::string searchTitle = parameterObject["title"].asString();
927   CVideoLibraryQueue::GetInstance().RefreshItem(CFileItemPtr(new CFileItem(infos)), ignoreNfo, true, false, searchTitle);
928 
929   return ACK;
930 }
931 
RemoveMovie(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)932 JSONRPC_STATUS CVideoLibrary::RemoveMovie(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
933 {
934   return RemoveVideo(parameterObject);
935 }
936 
RemoveTVShow(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)937 JSONRPC_STATUS CVideoLibrary::RemoveTVShow(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
938 {
939   return RemoveVideo(parameterObject);
940 }
941 
RemoveEpisode(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)942 JSONRPC_STATUS CVideoLibrary::RemoveEpisode(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
943 {
944   return RemoveVideo(parameterObject);
945 }
946 
RemoveMusicVideo(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)947 JSONRPC_STATUS CVideoLibrary::RemoveMusicVideo(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
948 {
949   return RemoveVideo(parameterObject);
950 }
951 
Scan(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)952 JSONRPC_STATUS CVideoLibrary::Scan(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
953 {
954   std::string directory = parameterObject["directory"].asString();
955   std::string cmd = StringUtils::Format("updatelibrary(video, %s, %s)", StringUtils::Paramify(directory).c_str(), parameterObject["showdialogs"].asBoolean() ? "true" : "false");
956 
957   CApplicationMessenger::GetInstance().SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);
958   return ACK;
959 }
960 
Export(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)961 JSONRPC_STATUS CVideoLibrary::Export(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
962 {
963   std::string cmd;
964   if (parameterObject["options"].isMember("path"))
965     cmd = StringUtils::Format("exportlibrary2(video, singlefile, %s)", StringUtils::Paramify(parameterObject["options"]["path"].asString()).c_str());
966   else
967   {
968     cmd = "exportlibrary2(video, separate, dummy";
969     if (parameterObject["options"]["images"].isBoolean() &&
970         parameterObject["options"]["images"].asBoolean() == true)
971       cmd += ", artwork";
972     if (parameterObject["options"]["overwrite"].isBoolean() &&
973         parameterObject["options"]["overwrite"].asBoolean() == true)
974       cmd += ", overwrite";
975     if (parameterObject["options"]["actorthumbs"].isBoolean() &&
976         parameterObject["options"]["actorthumbs"].asBoolean() == true)
977       cmd += ", actorthumbs";
978     cmd += ")";
979   }
980 
981   CApplicationMessenger::GetInstance().SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);
982   return ACK;
983 }
984 
Clean(const std::string & method,ITransportLayer * transport,IClient * client,const CVariant & parameterObject,CVariant & result)985 JSONRPC_STATUS CVideoLibrary::Clean(const std::string &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
986 {
987   std::string directory = parameterObject["directory"].asString();
988   std::string cmd;
989   if (parameterObject["content"].empty())
990     cmd = StringUtils::Format("cleanlibrary(video, {0}, {1})",
991                               parameterObject["showdialogs"].asBoolean() ? "true" : "false",
992                               StringUtils::Paramify(directory).c_str());
993   else
994     cmd = StringUtils::Format("cleanlibrary({0}, {1}, {2})", parameterObject["content"].asString(),
995                               parameterObject["showdialogs"].asBoolean() ? "true" : "false",
996                               StringUtils::Paramify(directory).c_str());
997 
998   CApplicationMessenger::GetInstance().SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, cmd);
999   return ACK;
1000 }
1001 
FillFileItem(const std::string & strFilename,CFileItemPtr & item,const CVariant & parameterObject)1002 bool CVideoLibrary::FillFileItem(const std::string &strFilename, CFileItemPtr &item, const CVariant &parameterObject /* = CVariant(CVariant::VariantTypeArray) */)
1003 {
1004   CVideoDatabase videodatabase;
1005   if (strFilename.empty())
1006     return false;
1007 
1008   bool filled = false;
1009   if (videodatabase.Open())
1010   {
1011     CVideoInfoTag details;
1012     if (videodatabase.LoadVideoInfo(strFilename, details))
1013     {
1014       item->SetFromVideoInfoTag(details);
1015       item->SetDynPath(strFilename);
1016       filled = true;
1017     }
1018   }
1019 
1020   if (item->GetLabel().empty())
1021   {
1022     item->SetLabel(CUtil::GetTitleFromPath(strFilename, false));
1023     if (item->GetLabel().empty())
1024       item->SetLabel(URIUtils::GetFileName(strFilename));
1025   }
1026 
1027   return filled;
1028 }
1029 
FillFileItemList(const CVariant & parameterObject,CFileItemList & list)1030 bool CVideoLibrary::FillFileItemList(const CVariant &parameterObject, CFileItemList &list)
1031 {
1032   CVideoDatabase videodatabase;
1033   if (!videodatabase.Open())
1034     return false;
1035 
1036   std::string file = parameterObject["file"].asString();
1037   int movieID = (int)parameterObject["movieid"].asInteger(-1);
1038   int episodeID = (int)parameterObject["episodeid"].asInteger(-1);
1039   int musicVideoID = (int)parameterObject["musicvideoid"].asInteger(-1);
1040 
1041   bool success = false;
1042   CFileItemPtr fileItem(new CFileItem());
1043   if (FillFileItem(file, fileItem))
1044   {
1045     success = true;
1046     list.Add(fileItem);
1047   }
1048 
1049   if (movieID > 0)
1050   {
1051     CVideoInfoTag details;
1052     videodatabase.GetMovieInfo("", details, movieID);
1053     if (!details.IsEmpty())
1054     {
1055       list.Add(CFileItemPtr(new CFileItem(details)));
1056       success = true;
1057     }
1058   }
1059   if (episodeID > 0)
1060   {
1061     CVideoInfoTag details;
1062     if (videodatabase.GetEpisodeInfo("", details, episodeID) && !details.IsEmpty())
1063     {
1064       list.Add(CFileItemPtr(new CFileItem(details)));
1065       success = true;
1066     }
1067   }
1068   if (musicVideoID > 0)
1069   {
1070     CVideoInfoTag details;
1071     videodatabase.GetMusicVideoInfo("", details, musicVideoID);
1072     if (!details.IsEmpty())
1073     {
1074       list.Add(CFileItemPtr(new CFileItem(details)));
1075       success = true;
1076     }
1077   }
1078 
1079   return success;
1080 }
1081 
RequiresAdditionalDetails(const MediaType & mediaType,const CVariant & parameterObject)1082 int CVideoLibrary::RequiresAdditionalDetails(const MediaType& mediaType, const CVariant &parameterObject)
1083 {
1084   if (mediaType != MediaTypeMovie && mediaType != MediaTypeTvShow && mediaType != MediaTypeEpisode && mediaType != MediaTypeMusicVideo)
1085     return VideoDbDetailsNone;
1086 
1087   const CVariant& properties = parameterObject["properties"];
1088   int details = VideoDbDetailsNone;
1089   for (CVariant::const_iterator_array itr = properties.begin_array(); itr != properties.end_array(); itr++)
1090   {
1091     std::string propertyValue = itr->asString();
1092     if (propertyValue == "cast")
1093       details = details | VideoDbDetailsCast;
1094     else if (propertyValue == "ratings")
1095       details = details | VideoDbDetailsRating;
1096     else if (propertyValue == "uniqueid")
1097       details = details | VideoDbDetailsUniqueID;
1098     else if (propertyValue == "showlink")
1099       details = details | VideoDbDetailsShowLink;
1100     else if (propertyValue == "streamdetails")
1101       details = details | VideoDbDetailsStream;
1102     else if (propertyValue == "tag")
1103       details = details | VideoDbDetailsTag;
1104   }
1105   return details;
1106 }
1107 
HandleItems(const char * idProperty,const char * resultName,CFileItemList & items,const CVariant & parameterObject,CVariant & result,bool limit)1108 JSONRPC_STATUS CVideoLibrary::HandleItems(const char *idProperty, const char *resultName, CFileItemList &items, const CVariant &parameterObject, CVariant &result, bool limit /* = true */)
1109 {
1110   int size = items.Size();
1111   if (!limit && items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
1112     size = (int)items.GetProperty("total").asInteger();
1113   HandleFileItemList(idProperty, true, resultName, items, parameterObject, result, size, limit);
1114 
1115   return OK;
1116 }
1117 
RemoveVideo(const CVariant & parameterObject)1118 JSONRPC_STATUS CVideoLibrary::RemoveVideo(const CVariant &parameterObject)
1119 {
1120   CVideoDatabase videodatabase;
1121   if (!videodatabase.Open())
1122     return InternalError;
1123 
1124   if (parameterObject.isMember("movieid"))
1125     videodatabase.DeleteMovie((int)parameterObject["movieid"].asInteger());
1126   else if (parameterObject.isMember("tvshowid"))
1127     videodatabase.DeleteTvShow((int)parameterObject["tvshowid"].asInteger());
1128   else if (parameterObject.isMember("episodeid"))
1129     videodatabase.DeleteEpisode((int)parameterObject["episodeid"].asInteger());
1130   else if (parameterObject.isMember("musicvideoid"))
1131     videodatabase.DeleteMusicVideo((int)parameterObject["musicvideoid"].asInteger());
1132 
1133   CJSONRPCUtils::NotifyItemUpdated();
1134   return ACK;
1135 }
1136 
UpdateResumePoint(const CVariant & parameterObject,CVideoInfoTag & details,CVideoDatabase & videodatabase)1137 void CVideoLibrary::UpdateResumePoint(const CVariant &parameterObject, CVideoInfoTag &details, CVideoDatabase &videodatabase)
1138 {
1139   if (!parameterObject["resume"].isNull())
1140   {
1141     double position = (double)parameterObject["resume"]["position"].asDouble();
1142     if (position == 0.0)
1143       videodatabase.ClearBookMarksOfFile(details.m_strFileNameAndPath, CBookmark::RESUME);
1144     else
1145     {
1146       CBookmark bookmark;
1147       double total = (double)parameterObject["resume"]["total"].asDouble();
1148       if (total <= 0.0 && !videodatabase.GetResumeBookMark(details.m_strFileNameAndPath, bookmark))
1149         bookmark.totalTimeInSeconds = details.m_streamDetails.GetVideoDuration();
1150       else
1151         bookmark.totalTimeInSeconds = total;
1152 
1153       bookmark.timeInSeconds = position;
1154       videodatabase.AddBookMarkToFile(details.m_strFileNameAndPath, bookmark, CBookmark::RESUME);
1155     }
1156   }
1157 }
1158 
UpdateVideoTagField(const CVariant & parameterObject,const std::string & fieldName,std::vector<std::string> & fieldValue,std::set<std::string> & updatedDetails)1159 void CVideoLibrary::UpdateVideoTagField(const CVariant& parameterObject, const std::string& fieldName, std::vector<std::string>& fieldValue, std::set<std::string>& updatedDetails)
1160 {
1161   if (ParameterNotNull(parameterObject, fieldName))
1162   {
1163     CopyStringArray(parameterObject[fieldName], fieldValue);
1164     updatedDetails.insert(fieldName);
1165   }
1166 }
1167 
UpdateVideoTag(const CVariant & parameterObject,CVideoInfoTag & details,std::map<std::string,std::string> & artwork,std::set<std::string> & removedArtwork,std::set<std::string> & updatedDetails)1168 void CVideoLibrary::UpdateVideoTag(const CVariant &parameterObject, CVideoInfoTag& details, std::map<std::string, std::string> &artwork, std::set<std::string> &removedArtwork, std::set<std::string> &updatedDetails)
1169 {
1170   if (ParameterNotNull(parameterObject, "title"))
1171     details.SetTitle(parameterObject["title"].asString());
1172   if (ParameterNotNull(parameterObject, "playcount"))
1173     details.SetPlayCount(static_cast<int>(parameterObject["playcount"].asInteger()));
1174   if (ParameterNotNull(parameterObject, "runtime"))
1175     details.SetDuration(static_cast<int>(parameterObject["runtime"].asInteger()));
1176 
1177   std::vector<std::string> director(details.m_director);
1178   UpdateVideoTagField(parameterObject, "director", director, updatedDetails);
1179   details.SetDirector(director);
1180 
1181   std::vector<std::string> studio(details.m_studio);
1182   UpdateVideoTagField(parameterObject, "studio", studio, updatedDetails);
1183   details.SetStudio(studio);
1184 
1185   if (ParameterNotNull(parameterObject, "plot"))
1186     details.SetPlot(parameterObject["plot"].asString());
1187   if (ParameterNotNull(parameterObject, "album"))
1188     details.SetAlbum(parameterObject["album"].asString());
1189 
1190   std::vector<std::string> artist(details.m_artist);
1191   UpdateVideoTagField(parameterObject, "artist", artist, updatedDetails);
1192   details.SetArtist(artist);
1193 
1194   std::vector<std::string> genre(details.m_genre);
1195   UpdateVideoTagField(parameterObject, "genre", genre, updatedDetails);
1196   details.SetGenre(genre);
1197 
1198   if (ParameterNotNull(parameterObject, "track"))
1199     details.m_iTrack = (int)parameterObject["track"].asInteger();
1200   if (ParameterNotNull(parameterObject, "rating"))
1201   {
1202     details.SetRating(parameterObject["rating"].asFloat());
1203     updatedDetails.insert("ratings");
1204   }
1205   if (ParameterNotNull(parameterObject, "votes"))
1206   {
1207     details.SetVotes(StringUtils::ReturnDigits(parameterObject["votes"].asString()));
1208     updatedDetails.insert("ratings"); //Votes and ratings both need updates now, this will trigger those
1209   }
1210   if (ParameterNotNull(parameterObject, "ratings"))
1211   {
1212     CVariant ratings = parameterObject["ratings"];
1213     for (CVariant::const_iterator_map rIt = ratings.begin_map(); rIt != ratings.end_map(); rIt++)
1214     {
1215       if (rIt->second.isObject() && ParameterNotNull(rIt->second, "rating"))
1216       {
1217         const auto& rating = rIt->second;
1218         if (ParameterNotNull(rating, "votes"))
1219         {
1220           details.SetRating(rating["rating"].asFloat(),
1221                             static_cast<int>(rating["votes"].asInteger()),
1222                             rIt->first,
1223                             (ParameterNotNull(rating, "default") && rating["default"].asBoolean()));
1224         }
1225         else
1226           details.SetRating(rating["rating"].asFloat(), rIt->first, (ParameterNotNull(rating, "default") && rating["default"].asBoolean()));
1227 
1228         updatedDetails.insert("ratings");
1229       }
1230       else if (rIt->second.isNull())
1231       {
1232         details.RemoveRating(rIt->first);
1233         updatedDetails.insert("ratings");
1234       }
1235     }
1236   }
1237   if (ParameterNotNull(parameterObject, "userrating"))
1238     details.m_iUserRating = static_cast<int>(parameterObject["userrating"].asInteger());
1239   if (ParameterNotNull(parameterObject, "mpaa"))
1240     details.SetMPAARating(parameterObject["mpaa"].asString());
1241   if (ParameterNotNull(parameterObject, "imdbnumber"))
1242   {
1243     details.SetUniqueID(parameterObject["imdbnumber"].asString());
1244     updatedDetails.insert("uniqueid");
1245   }
1246   if (ParameterNotNull(parameterObject, "uniqueid"))
1247   {
1248     CVariant uniqueids = parameterObject["uniqueid"];
1249     for (CVariant::const_iterator_map idIt = uniqueids.begin_map(); idIt != uniqueids.end_map(); idIt++)
1250     {
1251       if (idIt->second.isString() && !idIt->second.asString().empty())
1252       {
1253         details.SetUniqueID(idIt->second.asString(), idIt->first);
1254         updatedDetails.insert("uniqueid");
1255       }
1256       else if (idIt->second.isNull() && idIt->first != details.GetDefaultUniqueID())
1257       {
1258         details.RemoveUniqueID(idIt->first);
1259         updatedDetails.insert("uniqueid");
1260       }
1261     }
1262   }
1263   if (ParameterNotNull(parameterObject, "premiered"))
1264   {
1265     CDateTime premiered;
1266     SetFromDBDate(parameterObject["premiered"], premiered);
1267     details.SetPremiered(premiered);
1268   }
1269   else if (ParameterNotNull(parameterObject, "year"))
1270     details.SetYear((int)parameterObject["year"].asInteger());
1271   if (ParameterNotNull(parameterObject, "lastplayed"))
1272     SetFromDBDateTime(parameterObject["lastplayed"], details.m_lastPlayed);
1273   if (ParameterNotNull(parameterObject, "firstaired"))
1274     SetFromDBDate(parameterObject["firstaired"], details.m_firstAired);
1275   if (ParameterNotNull(parameterObject, "productioncode"))
1276     details.SetProductionCode(parameterObject["productioncode"].asString());
1277   if (ParameterNotNull(parameterObject, "season"))
1278     details.m_iSeason = (int)parameterObject["season"].asInteger();
1279   if (ParameterNotNull(parameterObject, "episode"))
1280     details.m_iEpisode = (int)parameterObject["episode"].asInteger();
1281   if (ParameterNotNull(parameterObject, "originaltitle"))
1282     details.SetOriginalTitle(parameterObject["originaltitle"].asString());
1283   if (ParameterNotNull(parameterObject, "trailer"))
1284     details.SetTrailer(parameterObject["trailer"].asString());
1285   if (ParameterNotNull(parameterObject, "tagline"))
1286     details.SetTagLine(parameterObject["tagline"].asString());
1287   if (ParameterNotNull(parameterObject, "status"))
1288     details.SetStatus(parameterObject["status"].asString());
1289   if (ParameterNotNull(parameterObject, "plotoutline"))
1290     details.SetPlotOutline(parameterObject["plotoutline"].asString());
1291 
1292   std::vector<std::string> credits(details.m_writingCredits);
1293   UpdateVideoTagField(parameterObject, "writer", credits, updatedDetails);
1294   details.SetWritingCredits(credits);
1295 
1296   std::vector<std::string> country(details.m_country);
1297   UpdateVideoTagField(parameterObject, "country", country, updatedDetails);
1298   details.SetCountry(country);
1299 
1300   if (ParameterNotNull(parameterObject, "top250"))
1301     details.m_iTop250 = (int)parameterObject["top250"].asInteger();
1302   if (ParameterNotNull(parameterObject, "sorttitle"))
1303     details.SetSortTitle(parameterObject["sorttitle"].asString());
1304   if (ParameterNotNull(parameterObject, "episodeguide"))
1305     details.SetEpisodeGuide(parameterObject["episodeguide"].asString());
1306   if (ParameterNotNull(parameterObject, "set"))
1307   {
1308     details.SetSet(parameterObject["set"].asString());
1309     updatedDetails.insert("set");
1310   }
1311 
1312   std::vector<std::string> showLink(details.m_showLink);
1313   UpdateVideoTagField(parameterObject, "showlink", showLink, updatedDetails);
1314   details.SetShowLink(showLink);
1315 
1316   std::vector<std::string> tags(details.m_tags);
1317   UpdateVideoTagField(parameterObject, "tag", tags, updatedDetails);
1318   details.SetTags(tags);
1319 
1320   if (ParameterNotNull(parameterObject, "thumbnail"))
1321   {
1322     std::string value = parameterObject["thumbnail"].asString();
1323     artwork["thumb"] = StringUtils::Trim(value);
1324     updatedDetails.insert("art.altered");
1325   }
1326   if (ParameterNotNull(parameterObject, "fanart"))
1327   {
1328     std::string value = parameterObject["fanart"].asString();
1329     artwork["fanart"] = StringUtils::Trim(value);
1330     updatedDetails.insert("art.altered");
1331   }
1332 
1333   if (ParameterNotNull(parameterObject, "art"))
1334   {
1335     CVariant art = parameterObject["art"];
1336     for (CVariant::const_iterator_map artIt = art.begin_map(); artIt != art.end_map(); artIt++)
1337     {
1338       if (artIt->second.isString() && !artIt->second.asString().empty())
1339       {
1340         artwork[artIt->first] = CTextureUtils::UnwrapImageURL(artIt->second.asString());
1341         updatedDetails.insert("art.altered");
1342       }
1343       else if (artIt->second.isNull())
1344       {
1345         artwork.erase(artIt->first);
1346         removedArtwork.insert(artIt->first);
1347       }
1348     }
1349   }
1350 
1351   if (ParameterNotNull(parameterObject, "dateadded"))
1352   {
1353     SetFromDBDateTime(parameterObject["dateadded"], details.m_dateAdded);
1354     updatedDetails.insert("dateadded");
1355   }
1356 }
1357