[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / interfaces / json-rpc / VideoLibrary.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "VideoLibrary.h"
22 #include "ApplicationMessenger.h"
23 #include "TextureDatabase.h"
24 #include "Util.h"
25 #include "utils/StringUtils.h"
26 #include "utils/URIUtils.h"
27 #include "video/VideoDatabase.h"
28
29 using namespace JSONRPC;
30
31 JSONRPC_STATUS CVideoLibrary::GetMovies(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
32 {
33   CVideoDatabase videodatabase;
34   if (!videodatabase.Open())
35     return InternalError;
36
37   SortDescription sorting;
38   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
39   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
40     return InvalidParams;
41
42   CVideoDbUrl videoUrl;
43   videoUrl.FromString("videodb://movies/titles/");
44   int genreID = -1, year = -1, setID = 0;
45   const CVariant &filter = parameterObject["filter"];
46   if (filter.isMember("genreid"))
47     genreID = (int)filter["genreid"].asInteger();
48   else if (filter.isMember("genre"))
49     videoUrl.AddOption("genre", filter["genre"].asString());
50   else if (filter.isMember("year"))
51     year = (int)filter["year"].asInteger();
52   else if (filter.isMember("actor"))
53     videoUrl.AddOption("actor", filter["actor"].asString());
54   else if (filter.isMember("director"))
55     videoUrl.AddOption("director", filter["director"].asString());
56   else if (filter.isMember("studio"))
57     videoUrl.AddOption("studio", filter["studio"].asString());
58   else if (filter.isMember("country"))
59     videoUrl.AddOption("country", filter["country"].asString());
60   else if (filter.isMember("setid"))
61     setID = (int)filter["setid"].asInteger();
62   else if (filter.isMember("set"))
63     videoUrl.AddOption("set", filter["set"].asString());
64   else if (filter.isMember("tag"))
65     videoUrl.AddOption("tag", filter["tag"].asString());
66   else if (filter.isObject())
67   {
68     CStdString xsp;
69     if (!GetXspFiltering("movies", filter, xsp))
70       return InvalidParams;
71
72     videoUrl.AddOption("xsp", xsp);
73   }
74
75   // setID must not be -1 otherwise GetMoviesNav() will return sets
76   if (setID < 0)
77     setID = 0;
78
79   CFileItemList items;
80   if (!videodatabase.GetMoviesNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, setID, -1, sorting))
81     return InvalidParams;
82
83   return GetAdditionalMovieDetails(parameterObject, items, result, videodatabase, false);
84 }
85
86 JSONRPC_STATUS CVideoLibrary::GetMovieDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
87 {
88   int id = (int)parameterObject["movieid"].asInteger();
89
90   CVideoDatabase videodatabase;
91   if (!videodatabase.Open())
92     return InternalError;
93
94   CVideoInfoTag infos;
95   if (!videodatabase.GetMovieInfo("", infos, id) || infos.m_iDbId <= 0)
96     return InvalidParams;
97
98   HandleFileItem("movieid", true, "moviedetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
99   return OK;
100 }
101
102 JSONRPC_STATUS CVideoLibrary::GetMovieSets(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
103 {
104   CVideoDatabase videodatabase;
105   if (!videodatabase.Open())
106     return InternalError;
107
108   CFileItemList items;
109   if (!videodatabase.GetSetsNav("videodb://movies/sets/", items, VIDEODB_CONTENT_MOVIES))
110     return InternalError;
111
112   HandleFileItemList("setid", false, "sets", items, parameterObject, result);
113   return OK;
114 }
115
116 JSONRPC_STATUS CVideoLibrary::GetMovieSetDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
117 {
118   int id = (int)parameterObject["setid"].asInteger();
119
120   CVideoDatabase videodatabase;
121   if (!videodatabase.Open())
122     return InternalError;
123
124   // Get movie set details
125   CVideoInfoTag infos;
126   if (!videodatabase.GetSetInfo(id, infos) || infos.m_iDbId <= 0)
127     return InvalidParams;
128
129   HandleFileItem("setid", false, "setdetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
130
131   // Get movies from the set
132   CFileItemList items;
133   if (!videodatabase.GetMoviesNav("videodb://movies/titles/", items, -1, -1, -1, -1, -1, -1, id))
134     return InternalError;
135
136   return GetAdditionalMovieDetails(parameterObject["movies"], items, result["setdetails"], videodatabase, true);
137 }
138
139 JSONRPC_STATUS CVideoLibrary::GetTVShows(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
140 {
141   CVideoDatabase videodatabase;
142   if (!videodatabase.Open())
143     return InternalError;
144
145   SortDescription sorting;
146   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
147   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
148     return InvalidParams;
149
150   CVideoDbUrl videoUrl;
151   videoUrl.FromString("videodb://tvshows/titles/");
152   int genreID = -1, year = -1;
153   const CVariant &filter = parameterObject["filter"];
154   if (filter.isMember("genreid"))
155     genreID = (int)filter["genreid"].asInteger();
156   else if (filter.isMember("genre"))
157     videoUrl.AddOption("genre", filter["genre"].asString());
158   else if (filter.isMember("year"))
159     year = (int)filter["year"].asInteger();
160   else if (filter.isMember("actor"))
161     videoUrl.AddOption("actor", filter["actor"].asString());
162   else if (filter.isMember("studio"))
163     videoUrl.AddOption("studio", filter["studio"].asString());
164   else if (filter.isMember("tag"))
165     videoUrl.AddOption("tag", filter["tag"].asString());
166   else if (filter.isObject())
167   {
168     CStdString xsp;
169     if (!GetXspFiltering("tvshows", filter, xsp))
170       return InvalidParams;
171
172     videoUrl.AddOption("xsp", xsp);
173   }
174
175   CFileItemList items;
176   if (!videodatabase.GetTvShowsNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, sorting))
177     return InvalidParams;
178
179   bool additionalInfo = false;
180   for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
181   {
182     CStdString fieldValue = itr->asString();
183     if (fieldValue == "cast" || fieldValue == "tag")
184       additionalInfo = true;
185   }
186
187   if (additionalInfo)
188   {
189     for (int index = 0; index < items.Size(); index++)
190       videodatabase.GetTvShowInfo("", *(items[index]->GetVideoInfoTag()), items[index]->GetVideoInfoTag()->m_iDbId);
191   }
192
193   int size = items.Size();
194   if (items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
195     size = (int)items.GetProperty("total").asInteger();
196   HandleFileItemList("tvshowid", true, "tvshows", items, parameterObject, result, size, false);
197
198   return OK;
199 }
200
201 JSONRPC_STATUS CVideoLibrary::GetTVShowDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
202 {
203   CVideoDatabase videodatabase;
204   if (!videodatabase.Open())
205     return InternalError;
206
207   int id = (int)parameterObject["tvshowid"].asInteger();
208
209   CVideoInfoTag infos;
210   if (!videodatabase.GetTvShowInfo("", infos, id) || infos.m_iDbId <= 0)
211     return InvalidParams;
212
213   HandleFileItem("tvshowid", true, "tvshowdetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
214   return OK;
215 }
216
217 JSONRPC_STATUS CVideoLibrary::GetSeasons(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
218 {
219   CVideoDatabase videodatabase;
220   if (!videodatabase.Open())
221     return InternalError;
222
223   int tvshowID = (int)parameterObject["tvshowid"].asInteger();
224
225   CStdString strPath = StringUtils::Format("videodb://tvshows/titles/%i/", tvshowID);
226   CFileItemList items;
227   if (!videodatabase.GetSeasonsNav(strPath, items, -1, -1, -1, -1, tvshowID, false))
228     return InternalError;
229
230   HandleFileItemList("seasonid", false, "seasons", items, parameterObject, result);
231   return OK;
232 }
233
234 JSONRPC_STATUS CVideoLibrary::GetSeasonDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
235 {
236   CVideoDatabase videodatabase;
237   if (!videodatabase.Open())
238     return InternalError;
239
240   int id = (int)parameterObject["seasonid"].asInteger();
241
242   CVideoInfoTag infos;
243   if (!videodatabase.GetSeasonInfo(id, infos) ||
244       infos.m_iDbId <= 0 || infos.m_iIdShow <= 0)
245     return InvalidParams;
246   
247   CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos));
248   HandleFileItem("seasonid", false, "seasondetails", pItem, parameterObject, parameterObject["properties"], result, false);
249   return OK;
250 }
251
252 JSONRPC_STATUS CVideoLibrary::GetEpisodes(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
253 {
254   CVideoDatabase videodatabase;
255   if (!videodatabase.Open())
256     return InternalError;
257
258   SortDescription sorting;
259   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
260   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
261     return InvalidParams;
262
263   int tvshowID = (int)parameterObject["tvshowid"].asInteger();
264   int season   = (int)parameterObject["season"].asInteger();
265   
266   CStdString strPath = StringUtils::Format("videodb://tvshows/titles/%i/%i/", tvshowID, season);
267
268   CVideoDbUrl videoUrl;
269   videoUrl.FromString(strPath);
270   const CVariant &filter = parameterObject["filter"];
271   if (filter.isMember("genreid"))
272     videoUrl.AddOption("genreid", (int)filter["genreid"].asInteger());
273   else if (filter.isMember("genre"))
274     videoUrl.AddOption("genre", filter["genre"].asString());
275   else if (filter.isMember("year"))
276     videoUrl.AddOption("year", (int)filter["year"].asInteger());
277   else if (filter.isMember("actor"))
278     videoUrl.AddOption("actor", filter["actor"].asString());
279   else if (filter.isMember("director"))
280     videoUrl.AddOption("director", filter["director"].asString());
281   else if (filter.isObject())
282   {
283     CStdString xsp;
284     if (!GetXspFiltering("episodes", filter, xsp))
285       return InvalidParams;
286
287     videoUrl.AddOption("xsp", xsp);
288   }
289
290   if (tvshowID <= 0 && (season > 0 || videoUrl.HasOption("genreid") || videoUrl.HasOption("genre") || videoUrl.HasOption("actor")))
291     return InvalidParams;
292
293   if (tvshowID > 0)
294   {
295     videoUrl.AddOption("tvshowid", tvshowID);
296     if (season >= 0)
297       videoUrl.AddOption("season", season);
298   }
299
300   CFileItemList items;
301   if (!videodatabase.GetEpisodesByWhere(videoUrl.ToString(), CDatabase::Filter(), items, false, sorting))
302     return InvalidParams;
303
304   return GetAdditionalEpisodeDetails(parameterObject, items, result, videodatabase, false);
305 }
306
307 JSONRPC_STATUS CVideoLibrary::GetEpisodeDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
308 {
309   CVideoDatabase videodatabase;
310   if (!videodatabase.Open())
311     return InternalError;
312
313   int id = (int)parameterObject["episodeid"].asInteger();
314
315   CVideoInfoTag infos;
316   if (!videodatabase.GetEpisodeInfo("", infos, id) || infos.m_iDbId <= 0)
317     return InvalidParams;
318
319   CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos));
320   // We need to set the correct base path to get the valid fanart
321   int tvshowid = infos.m_iIdShow;
322   if (tvshowid <= 0)
323     tvshowid = videodatabase.GetTvShowForEpisode(id);
324
325   CStdString basePath = StringUtils::Format("videodb://tvshows/titles/%ld/%ld/%ld", tvshowid, infos.m_iSeason, id);
326   pItem->SetPath(basePath);
327
328   HandleFileItem("episodeid", true, "episodedetails", pItem, parameterObject, parameterObject["properties"], result, false);
329   return OK;
330 }
331
332 JSONRPC_STATUS CVideoLibrary::GetMusicVideos(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
333 {
334   CVideoDatabase videodatabase;
335   if (!videodatabase.Open())
336     return InternalError;
337
338   SortDescription sorting;
339   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
340   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
341     return InvalidParams;
342
343   CVideoDbUrl videoUrl;
344   videoUrl.FromString("videodb://musicvideos/titles/");
345   int genreID = -1, year = -1;
346   const CVariant &filter = parameterObject["filter"];
347   if (filter.isMember("artist"))
348     videoUrl.AddOption("artist", filter["artist"].asString());
349   else if (filter.isMember("genreid"))
350     genreID = (int)filter["genreid"].asInteger();
351   else if (filter.isMember("genre"))
352     videoUrl.AddOption("genre", filter["genre"].asString());
353   else if (filter.isMember("year"))
354     year = (int)filter["year"].asInteger();
355   else if (filter.isMember("director"))
356     videoUrl.AddOption("director", filter["director"].asString());
357   else if (filter.isMember("studio"))
358     videoUrl.AddOption("studio", filter["studio"].asString());
359   else if (filter.isMember("tag"))
360     videoUrl.AddOption("tag", filter["tag"].asString());
361   else if (filter.isObject())
362   {
363     CStdString xsp;
364     if (!GetXspFiltering("musicvideos", filter, xsp))
365       return InvalidParams;
366
367     videoUrl.AddOption("xsp", xsp);
368   }
369
370   CFileItemList items;
371   if (!videodatabase.GetMusicVideosNav(videoUrl.ToString(), items, genreID, year, -1, -1, -1, -1, -1, sorting))
372     return InternalError;
373
374   return GetAdditionalMusicVideoDetails(parameterObject, items, result, videodatabase, false);
375 }
376
377 JSONRPC_STATUS CVideoLibrary::GetMusicVideoDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
378 {
379   CVideoDatabase videodatabase;
380   if (!videodatabase.Open())
381     return InternalError;
382
383   int id = (int)parameterObject["musicvideoid"].asInteger();
384
385   CVideoInfoTag infos;
386   if (!videodatabase.GetMusicVideoInfo("", infos, id) || infos.m_iDbId <= 0)
387     return InvalidParams;
388
389   HandleFileItem("musicvideoid", true, "musicvideodetails", CFileItemPtr(new CFileItem(infos)), parameterObject, parameterObject["properties"], result, false);
390   return OK;
391 }
392
393 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedMovies(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
394 {
395   CVideoDatabase videodatabase;
396   if (!videodatabase.Open())
397     return InternalError;
398
399   CFileItemList items;
400   if (!videodatabase.GetRecentlyAddedMoviesNav("videodb://recentlyaddedmovies/", items))
401     return InternalError;
402
403   return GetAdditionalMovieDetails(parameterObject, items, result, videodatabase, true);
404 }
405
406 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedEpisodes(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
407 {
408   CVideoDatabase videodatabase;
409   if (!videodatabase.Open())
410     return InternalError;
411
412   CFileItemList items;
413   if (!videodatabase.GetRecentlyAddedEpisodesNav("videodb://recentlyaddedepisodes/", items))
414     return InternalError;
415
416   return GetAdditionalEpisodeDetails(parameterObject, items, result, videodatabase, true);
417 }
418
419 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedMusicVideos(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
420 {
421   CVideoDatabase videodatabase;
422   if (!videodatabase.Open())
423     return InternalError;
424
425   CFileItemList items;
426   if (!videodatabase.GetRecentlyAddedMusicVideosNav("videodb://recentlyaddedmusicvideos/", items))
427     return InternalError;
428
429   return GetAdditionalMusicVideoDetails(parameterObject, items, result, videodatabase, true);
430 }
431
432 JSONRPC_STATUS CVideoLibrary::GetGenres(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
433 {
434   CStdString media = parameterObject["type"].asString();
435   media = media.ToLower();
436   int idContent = -1;
437
438   CStdString strPath = "videodb://";
439   /* select which video content to get genres from*/
440   if (media.Equals("movie"))
441   {
442     idContent = VIDEODB_CONTENT_MOVIES;
443     strPath += "movies";
444   }
445   else if (media.Equals("tvshow"))
446   {
447     idContent = VIDEODB_CONTENT_TVSHOWS;
448     strPath += "tvshows";
449   }
450   else if (media.Equals("musicvideo"))
451   {
452     idContent = VIDEODB_CONTENT_MUSICVIDEOS;
453     strPath += "musicvideos";
454   }
455   strPath += "/genres/";
456  
457   CVideoDatabase videodatabase;
458   if (!videodatabase.Open())
459     return InternalError;
460
461   CFileItemList items;
462   if (!videodatabase.GetGenresNav(strPath, items, idContent))
463     return InternalError;
464
465   /* need to set strTitle in each item*/
466   for (unsigned int i = 0; i < (unsigned int)items.Size(); i++)
467     items[i]->GetVideoInfoTag()->m_strTitle = items[i]->GetLabel();
468
469   HandleFileItemList("genreid", false, "genres", items, parameterObject, result);
470   return OK;
471 }
472
473 JSONRPC_STATUS CVideoLibrary::SetMovieDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
474 {
475   int id = (int)parameterObject["movieid"].asInteger();
476
477   CVideoDatabase videodatabase;
478   if (!videodatabase.Open())
479     return InternalError;
480
481   CVideoInfoTag infos;
482   if (!videodatabase.GetMovieInfo("", infos, id) || infos.m_iDbId <= 0)
483     return InvalidParams;
484
485   // get artwork
486   std::map<std::string, std::string> artwork;
487   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
488
489   int playcount = infos.m_playCount;
490   CDateTime lastPlayed = infos.m_lastPlayed;
491
492   std::set<std::string> removedArtwork;
493   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork);
494
495   // we need to manually remove tags/taglinks for now because they aren't replaced
496   // due to scrapers not supporting them
497   videodatabase.RemoveTagsFromItem(id, "movie");
498
499   if (videodatabase.SetDetailsForMovie(infos.m_strFileNameAndPath, infos, artwork, id) <= 0)
500     return InternalError;
501
502   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, "movie", removedArtwork))
503     return InternalError;
504
505   if (playcount != infos.m_playCount || lastPlayed != infos.m_lastPlayed)
506   {
507     // restore original playcount or the new one won't be announced
508     int newPlaycount = infos.m_playCount;
509     infos.m_playCount = playcount;
510     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed.IsValid() ? infos.m_lastPlayed : CDateTime::GetCurrentDateTime());
511   }
512
513   UpdateResumePoint(parameterObject, infos, videodatabase);
514
515   CJSONRPCUtils::NotifyItemUpdated();
516   return ACK;
517 }
518
519 JSONRPC_STATUS CVideoLibrary::SetMovieSetDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
520 {
521   int id = (int)parameterObject["setid"].asInteger();
522
523   CVideoDatabase videodatabase;
524   if (!videodatabase.Open())
525     return InternalError;
526
527   CVideoInfoTag infos;
528   videodatabase.GetSetInfo(id, infos);
529   if (infos.m_iDbId <= 0)
530   {
531     videodatabase.Close();
532     return InvalidParams;
533   }
534
535   // get artwork
536   std::map<std::string, std::string> artwork;
537   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
538
539   std::set<std::string> removedArtwork;
540   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork);
541
542   if (videodatabase.SetDetailsForMovieSet(infos, artwork, id) <= 0)
543     return InternalError;
544
545   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, "set", removedArtwork))
546     return InternalError;
547
548   CJSONRPCUtils::NotifyItemUpdated();
549   return ACK;
550 }
551
552 JSONRPC_STATUS CVideoLibrary::SetTVShowDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
553 {
554   int id = (int)parameterObject["tvshowid"].asInteger();
555
556   CVideoDatabase videodatabase;
557   if (!videodatabase.Open())
558     return InternalError;
559
560   CVideoInfoTag infos;
561   if (!videodatabase.GetTvShowInfo("", infos, id) || infos.m_iDbId <= 0)
562     return InvalidParams;
563
564   // get artwork
565   std::map<std::string, std::string> artwork;
566   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
567
568   std::map<int, std::map<std::string, std::string> > seasonArt;
569   videodatabase.GetTvShowSeasonArt(infos.m_iDbId, seasonArt);
570
571   int playcount = infos.m_playCount;
572   CDateTime lastPlayed = infos.m_lastPlayed;
573
574   std::set<std::string> removedArtwork;
575   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork);
576
577   // we need to manually remove tags/taglinks for now because they aren't replaced
578   // due to scrapers not supporting them
579   videodatabase.RemoveTagsFromItem(id, "tvshow");
580
581   if (videodatabase.SetDetailsForTvShow(infos.m_strFileNameAndPath, infos, artwork, seasonArt, id) <= 0)
582     return InternalError;
583
584   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, "tvshow", removedArtwork))
585     return InternalError;
586
587   if (playcount != infos.m_playCount || lastPlayed != infos.m_lastPlayed)
588   {
589     // restore original playcount or the new one won't be announced
590     int newPlaycount = infos.m_playCount;
591     infos.m_playCount = playcount;
592     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed.IsValid() ? infos.m_lastPlayed : CDateTime::GetCurrentDateTime());
593   }
594
595   CJSONRPCUtils::NotifyItemUpdated();
596   return ACK;
597 }
598
599 JSONRPC_STATUS CVideoLibrary::SetSeasonDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
600 {
601   int id = (int)parameterObject["seasonid"].asInteger();
602
603   CVideoDatabase videodatabase;
604   if (!videodatabase.Open())
605     return InternalError;
606
607   CVideoInfoTag infos;
608   videodatabase.GetSeasonInfo(id, infos);
609   if (infos.m_iDbId <= 0 || infos.m_iIdShow <= 0)
610   {
611     videodatabase.Close();
612     return InvalidParams;
613   }
614
615   // get artwork
616   std::map<std::string, std::string> artwork;
617   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
618
619   std::set<std::string> removedArtwork;
620   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork);
621
622   if (videodatabase.SetDetailsForSeason(infos, artwork, infos.m_iIdShow, id) <= 0)
623     return InternalError;
624
625   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, "season", removedArtwork))
626     return InternalError;
627
628   CJSONRPCUtils::NotifyItemUpdated();
629   return ACK;
630 }
631
632 JSONRPC_STATUS CVideoLibrary::SetEpisodeDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
633 {
634   int id = (int)parameterObject["episodeid"].asInteger();
635
636   CVideoDatabase videodatabase;
637   if (!videodatabase.Open())
638     return InternalError;
639
640   CVideoInfoTag infos;
641   videodatabase.GetEpisodeInfo("", infos, id);
642   if (infos.m_iDbId <= 0)
643   {
644     videodatabase.Close();
645     return InvalidParams;
646   }
647
648   int tvshowid = videodatabase.GetTvShowForEpisode(id);
649   if (tvshowid <= 0)
650   {
651     videodatabase.Close();
652     return InvalidParams;
653   }
654
655   // get artwork
656   std::map<std::string, std::string> artwork;
657   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
658
659   int playcount = infos.m_playCount;
660   CDateTime lastPlayed = infos.m_lastPlayed;
661
662   std::set<std::string> removedArtwork;
663   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork);
664
665   if (videodatabase.SetDetailsForEpisode(infos.m_strFileNameAndPath, infos, artwork, tvshowid, id) <= 0)
666     return InternalError;
667
668   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, "episode", removedArtwork))
669     return InternalError;
670
671   if (playcount != infos.m_playCount || lastPlayed != infos.m_lastPlayed)
672   {
673     // restore original playcount or the new one won't be announced
674     int newPlaycount = infos.m_playCount;
675     infos.m_playCount = playcount;
676     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed.IsValid() ? infos.m_lastPlayed : CDateTime::GetCurrentDateTime());
677   }
678
679   UpdateResumePoint(parameterObject, infos, videodatabase);
680
681   CJSONRPCUtils::NotifyItemUpdated();
682   return ACK;
683 }
684
685 JSONRPC_STATUS CVideoLibrary::SetMusicVideoDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
686 {
687   int id = (int)parameterObject["musicvideoid"].asInteger();
688
689   CVideoDatabase videodatabase;
690   if (!videodatabase.Open())
691     return InternalError;
692
693   CVideoInfoTag infos;
694   videodatabase.GetMusicVideoInfo("", infos, id);
695   if (infos.m_iDbId <= 0)
696   {
697     videodatabase.Close();
698     return InvalidParams;
699   }
700
701   // get artwork
702   std::map<std::string, std::string> artwork;
703   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
704
705   int playcount = infos.m_playCount;
706   CDateTime lastPlayed = infos.m_lastPlayed;
707
708   std::set<std::string> removedArtwork;
709   UpdateVideoTag(parameterObject, infos, artwork, removedArtwork);
710
711   // we need to manually remove tags/taglinks for now because they aren't replaced
712   // due to scrapers not supporting them
713   videodatabase.RemoveTagsFromItem(id, "musicvideo");
714
715   if (videodatabase.SetDetailsForMusicVideo(infos.m_strFileNameAndPath, infos, artwork, id) <= 0)
716     return InternalError;
717
718   if (!videodatabase.RemoveArtForItem(infos.m_iDbId, "musicvideo", removedArtwork))
719     return InternalError;
720
721   if (playcount != infos.m_playCount || lastPlayed != infos.m_lastPlayed)
722   {
723     // restore original playcount or the new one won't be announced
724     int newPlaycount = infos.m_playCount;
725     infos.m_playCount = playcount;
726     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed.IsValid() ? infos.m_lastPlayed : CDateTime::GetCurrentDateTime());
727   }
728
729   UpdateResumePoint(parameterObject, infos, videodatabase);
730
731   CJSONRPCUtils::NotifyItemUpdated();
732   return ACK;
733 }
734
735 JSONRPC_STATUS CVideoLibrary::RemoveMovie(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
736 {
737   return RemoveVideo(parameterObject);
738 }
739
740 JSONRPC_STATUS CVideoLibrary::RemoveTVShow(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
741 {
742   return RemoveVideo(parameterObject);
743 }
744
745 JSONRPC_STATUS CVideoLibrary::RemoveEpisode(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
746 {
747   return RemoveVideo(parameterObject);
748 }
749
750 JSONRPC_STATUS CVideoLibrary::RemoveMusicVideo(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
751 {
752   return RemoveVideo(parameterObject);
753 }
754
755 JSONRPC_STATUS CVideoLibrary::Scan(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
756 {
757   std::string directory = parameterObject["directory"].asString();
758   CStdString cmd;
759   if (directory.empty())
760     cmd = "updatelibrary(video)";
761   else
762     cmd = StringUtils::Format("updatelibrary(video, %s)", StringUtils::Paramify(directory).c_str());
763
764   CApplicationMessenger::Get().ExecBuiltIn(cmd);
765   return ACK;
766 }
767
768 JSONRPC_STATUS CVideoLibrary::Export(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
769 {
770   CStdString cmd;
771   if (parameterObject["options"].isMember("path"))
772     cmd = StringUtils::Format("exportlibrary(video, false, %s)", StringUtils::Paramify(parameterObject["options"]["path"].asString()).c_str());
773   else
774     cmd = StringUtils::Format("exportlibrary(video, true, %s, %s, %s)",
775                               parameterObject["options"]["images"].asBoolean() ? "true" : "false",
776                               parameterObject["options"]["overwrite"].asBoolean() ? "true" : "false",
777                               parameterObject["options"]["actorthumbs"].asBoolean() ? "true" : "false");
778
779   CApplicationMessenger::Get().ExecBuiltIn(cmd);
780   return ACK;
781 }
782
783 JSONRPC_STATUS CVideoLibrary::Clean(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
784 {
785   CApplicationMessenger::Get().ExecBuiltIn("cleanlibrary(video)");
786   return ACK;
787 }
788
789 bool CVideoLibrary::FillFileItem(const CStdString &strFilename, CFileItemPtr &item, const CVariant &parameterObject /* = CVariant(CVariant::VariantTypeArray) */)
790 {
791   CVideoDatabase videodatabase;
792   if (strFilename.empty())
793     return false;
794   
795   bool filled = false;
796   if (videodatabase.Open())
797   {
798     CVideoInfoTag details;
799     if (videodatabase.LoadVideoInfo(strFilename, details))
800     {
801       item->SetFromVideoInfoTag(details);
802       filled = true;
803     }
804   }
805
806   if (item->GetLabel().empty())
807   {
808     item->SetLabel(CUtil::GetTitleFromPath(strFilename, false));
809     if (item->GetLabel().empty())
810       item->SetLabel(URIUtils::GetFileName(strFilename));
811   }
812
813   return filled;
814 }
815
816 bool CVideoLibrary::FillFileItemList(const CVariant &parameterObject, CFileItemList &list)
817 {
818   CVideoDatabase videodatabase;
819   if (!videodatabase.Open())
820     return false;
821
822   CStdString file = parameterObject["file"].asString();
823   int movieID = (int)parameterObject["movieid"].asInteger(-1);
824   int episodeID = (int)parameterObject["episodeid"].asInteger(-1);
825   int musicVideoID = (int)parameterObject["musicvideoid"].asInteger(-1);
826
827   bool success = false;
828   CFileItemPtr fileItem(new CFileItem());
829   if (FillFileItem(file, fileItem))
830   {
831     success = true;
832     list.Add(fileItem);
833   }
834
835   if (movieID > 0)
836   {
837     CVideoInfoTag details;
838     videodatabase.GetMovieInfo("", details, movieID);
839     if (!details.IsEmpty())
840     {
841       list.Add(CFileItemPtr(new CFileItem(details)));
842       success = true;
843     }
844   }
845   if (episodeID > 0)
846   {
847     CVideoInfoTag details;
848     if (videodatabase.GetEpisodeInfo("", details, episodeID) && !details.IsEmpty())
849     {
850       list.Add(CFileItemPtr(new CFileItem(details)));
851       success = true;
852     }
853   }
854   if (musicVideoID > 0)
855   {
856     CVideoInfoTag details;
857     videodatabase.GetMusicVideoInfo("", details, musicVideoID);
858     if (!details.IsEmpty())
859     {
860       list.Add(CFileItemPtr(new CFileItem(details)));
861       success = true;
862     }
863   }
864
865   return success;
866 }
867
868 JSONRPC_STATUS CVideoLibrary::GetAdditionalMovieDetails(const CVariant &parameterObject, CFileItemList &items, CVariant &result, CVideoDatabase &videodatabase, bool limit /* = true */)
869 {
870   if (!videodatabase.Open())
871     return InternalError;
872
873   bool additionalInfo = false;
874   for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
875   {
876     CStdString fieldValue = itr->asString();
877     if (fieldValue == "cast" || fieldValue == "showlink" || fieldValue == "tag" || fieldValue == "streamdetails")
878       additionalInfo = true;
879   }
880
881   if (additionalInfo)
882   {
883     for (int index = 0; index < items.Size(); index++)
884     {
885       if (additionalInfo)
886         videodatabase.GetMovieInfo("", *(items[index]->GetVideoInfoTag()), items[index]->GetVideoInfoTag()->m_iDbId);
887     }
888   }
889
890   int size = items.Size();
891   if (!limit && items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
892     size = (int)items.GetProperty("total").asInteger();
893   HandleFileItemList("movieid", true, "movies", items, parameterObject, result, size, limit);
894
895   return OK;
896 }
897
898 JSONRPC_STATUS CVideoLibrary::GetAdditionalEpisodeDetails(const CVariant &parameterObject, CFileItemList &items, CVariant &result, CVideoDatabase &videodatabase, bool limit /* = true */)
899 {
900   if (!videodatabase.Open())
901     return InternalError;
902
903   bool additionalInfo = false;
904   for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
905   {
906     CStdString fieldValue = itr->asString();
907     if (fieldValue == "cast" || fieldValue == "streamdetails")
908       additionalInfo = true;
909   }
910
911   if (additionalInfo)
912   {
913     for (int index = 0; index < items.Size(); index++)
914     {
915       if (additionalInfo)
916         videodatabase.GetEpisodeInfo("", *(items[index]->GetVideoInfoTag()), items[index]->GetVideoInfoTag()->m_iDbId);
917     }
918   }
919   
920   int size = items.Size();
921   if (!limit && items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
922     size = (int)items.GetProperty("total").asInteger();
923   HandleFileItemList("episodeid", true, "episodes", items, parameterObject, result, size, limit);
924
925   return OK;
926 }
927
928 JSONRPC_STATUS CVideoLibrary::GetAdditionalMusicVideoDetails(const CVariant &parameterObject, CFileItemList &items, CVariant &result, CVideoDatabase &videodatabase, bool limit /* = true */)
929 {
930   if (!videodatabase.Open())
931     return InternalError;
932
933   bool streamdetails = false;
934   for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
935   {
936     if (itr->asString() == "tag" || itr->asString() == "streamdetails")
937       streamdetails = true;
938   }
939
940   if (streamdetails)
941   {
942     for (int index = 0; index < items.Size(); index++)
943       videodatabase.GetMusicVideoInfo("", *(items[index]->GetVideoInfoTag()), items[index]->GetVideoInfoTag()->m_iDbId);
944   }
945
946   int size = items.Size();
947   if (!limit && items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
948     size = (int)items.GetProperty("total").asInteger();
949   HandleFileItemList("musicvideoid", true, "musicvideos", items, parameterObject, result, size, limit);
950
951   return OK;
952 }
953
954 JSONRPC_STATUS CVideoLibrary::RemoveVideo(const CVariant &parameterObject)
955 {
956   CVideoDatabase videodatabase;
957   if (!videodatabase.Open())
958     return InternalError;
959
960   if (parameterObject.isMember("movieid"))
961     videodatabase.DeleteMovie((int)parameterObject["movieid"].asInteger());
962   else if (parameterObject.isMember("tvshowid"))
963     videodatabase.DeleteTvShow((int)parameterObject["tvshowid"].asInteger());
964   else if (parameterObject.isMember("episodeid"))
965     videodatabase.DeleteEpisode((int)parameterObject["episodeid"].asInteger());
966   else if (parameterObject.isMember("musicvideoid"))
967     videodatabase.DeleteMusicVideo((int)parameterObject["musicvideoid"].asInteger());
968
969   CJSONRPCUtils::NotifyItemUpdated();
970   return ACK;
971 }
972
973 void CVideoLibrary::UpdateResumePoint(const CVariant &parameterObject, CVideoInfoTag &details, CVideoDatabase &videodatabase)
974 {
975   if (!parameterObject["resume"].isNull())
976   {
977     int position = (int)parameterObject["resume"]["position"].asInteger();
978     if (position == 0)
979       videodatabase.ClearBookMarksOfFile(details.m_strFileNameAndPath, CBookmark::RESUME);
980     else
981     {
982       CBookmark bookmark;
983       if (videodatabase.GetResumeBookMark(details.m_strFileNameAndPath, bookmark))
984       {
985         bookmark.timeInSeconds = position;
986       
987         int total = (int)parameterObject["resume"]["total"].asInteger();
988         if (total > 0)
989           bookmark.totalTimeInSeconds = total;
990         videodatabase.AddBookMarkToFile(details.m_strFileNameAndPath, bookmark, CBookmark::RESUME);
991       }
992     }
993   }
994 }
995
996 void CVideoLibrary::UpdateVideoTag(const CVariant &parameterObject, CVideoInfoTag& details, std::map<std::string, std::string> &artwork, std::set<std::string> &removedArtwork)
997 {
998   if (ParameterNotNull(parameterObject, "title"))
999     details.m_strTitle = parameterObject["title"].asString();
1000   if (ParameterNotNull(parameterObject, "playcount"))
1001     details.m_playCount = (int)parameterObject["playcount"].asInteger();
1002   if (ParameterNotNull(parameterObject, "runtime"))
1003     details.m_duration = (int)parameterObject["runtime"].asInteger();
1004   if (ParameterNotNull(parameterObject, "director"))
1005     CopyStringArray(parameterObject["director"], details.m_director);
1006   if (ParameterNotNull(parameterObject, "studio"))
1007     CopyStringArray(parameterObject["studio"], details.m_studio);
1008   if (ParameterNotNull(parameterObject, "year"))
1009     details.m_iYear = (int)parameterObject["year"].asInteger();
1010   if (ParameterNotNull(parameterObject, "plot"))
1011     details.m_strPlot = parameterObject["plot"].asString();
1012   if (ParameterNotNull(parameterObject, "album"))
1013     details.m_strAlbum = parameterObject["album"].asString();
1014   if (ParameterNotNull(parameterObject, "artist"))
1015     CopyStringArray(parameterObject["artist"], details.m_artist);
1016   if (ParameterNotNull(parameterObject, "genre"))
1017     CopyStringArray(parameterObject["genre"], details.m_genre);
1018   if (ParameterNotNull(parameterObject, "track"))
1019     details.m_iTrack = (int)parameterObject["track"].asInteger();
1020   if (ParameterNotNull(parameterObject, "rating"))
1021     details.m_fRating = parameterObject["rating"].asFloat();
1022   if (ParameterNotNull(parameterObject, "mpaa"))
1023     details.m_strMPAARating = parameterObject["mpaa"].asString();
1024   if (ParameterNotNull(parameterObject, "imdbnumber"))
1025     details.m_strIMDBNumber = parameterObject["imdbnumber"].asString();
1026   if (ParameterNotNull(parameterObject, "premiered"))
1027     details.m_premiered.SetFromDBDate(parameterObject["premiered"].asString());
1028   if (ParameterNotNull(parameterObject, "votes"))
1029     details.m_strVotes = parameterObject["votes"].asString();
1030   if (ParameterNotNull(parameterObject, "lastplayed"))
1031     details.m_lastPlayed.SetFromDBDateTime(parameterObject["lastplayed"].asString());
1032   if (ParameterNotNull(parameterObject, "firstaired"))
1033     details.m_firstAired.SetFromDBDateTime(parameterObject["firstaired"].asString());
1034   if (ParameterNotNull(parameterObject, "productioncode"))
1035     details.m_strProductionCode = parameterObject["productioncode"].asString();
1036   if (ParameterNotNull(parameterObject, "season"))
1037     details.m_iSeason = (int)parameterObject["season"].asInteger();
1038   if (ParameterNotNull(parameterObject, "episode"))
1039     details.m_iEpisode = (int)parameterObject["episode"].asInteger();
1040   if (ParameterNotNull(parameterObject, "originaltitle"))
1041     details.m_strOriginalTitle = parameterObject["originaltitle"].asString();
1042   if (ParameterNotNull(parameterObject, "trailer"))
1043     details.m_strTrailer = parameterObject["trailer"].asString();
1044   if (ParameterNotNull(parameterObject, "tagline"))
1045     details.m_strTagLine = parameterObject["tagline"].asString();
1046   if (ParameterNotNull(parameterObject, "plotoutline"))
1047     details.m_strPlotOutline = parameterObject["plotoutline"].asString();
1048   if (ParameterNotNull(parameterObject, "writer"))
1049     CopyStringArray(parameterObject["writer"], details.m_writingCredits);
1050   if (ParameterNotNull(parameterObject, "country"))
1051     CopyStringArray(parameterObject["country"], details.m_country);
1052   if (ParameterNotNull(parameterObject, "top250"))
1053     details.m_iTop250 = (int)parameterObject["top250"].asInteger();
1054   if (ParameterNotNull(parameterObject, "sorttitle"))
1055     details.m_strSortTitle = parameterObject["sorttitle"].asString();
1056   if (ParameterNotNull(parameterObject, "episodeguide"))
1057     details.m_strEpisodeGuide = parameterObject["episodeguide"].asString();
1058   if (ParameterNotNull(parameterObject, "set"))
1059     details.m_strSet = parameterObject["set"].asString();
1060   if (ParameterNotNull(parameterObject, "showlink"))
1061     CopyStringArray(parameterObject["showlink"], details.m_showLink);
1062   if (ParameterNotNull(parameterObject, "thumbnail"))
1063     artwork["thumb"] = parameterObject["thumbnail"].asString();
1064   if (ParameterNotNull(parameterObject, "fanart"))
1065     artwork["fanart"] = parameterObject["fanart"].asString();
1066   if (ParameterNotNull(parameterObject, "tag"))
1067     CopyStringArray(parameterObject["tag"], details.m_tags);
1068   if (ParameterNotNull(parameterObject, "art"))
1069   {
1070     CVariant art = parameterObject["art"];
1071     for (CVariant::const_iterator_map artIt = art.begin_map(); artIt != art.end_map(); artIt++)
1072     {
1073       if (artIt->second.isString() && !artIt->second.asString().empty())
1074         artwork[artIt->first] = CTextureUtils::UnwrapImageURL(artIt->second.asString());
1075       else if (artIt->second.isNull())
1076       {
1077         artwork.erase(artIt->first);
1078         removedArtwork.insert(artIt->first);
1079       }
1080     }
1081   }
1082 }