[bluray] Fix stream info/language retrieval for blurays in non-nav mode.
[vuplus_xbmc] / xbmc / interfaces / json-rpc / VideoLibrary.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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 "TextureCache.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;
226   strPath.Format("videodb://tvshows/titles/%i/", tvshowID);
227   CFileItemList items;
228   if (!videodatabase.GetSeasonsNav(strPath, items, -1, -1, -1, -1, tvshowID, false))
229     return InternalError;
230
231   HandleFileItemList(NULL, false, "seasons", items, parameterObject, result);
232   return OK;
233 }
234
235 JSONRPC_STATUS CVideoLibrary::GetEpisodes(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
236 {
237   CVideoDatabase videodatabase;
238   if (!videodatabase.Open())
239     return InternalError;
240
241   SortDescription sorting;
242   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
243   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
244     return InvalidParams;
245
246   int tvshowID = (int)parameterObject["tvshowid"].asInteger();
247   int season   = (int)parameterObject["season"].asInteger();
248   
249   CStdString strPath;
250   strPath.Format("videodb://tvshows/titles/%i/%i/", tvshowID, season);
251
252   CVideoDbUrl videoUrl;
253   videoUrl.FromString(strPath);
254   const CVariant &filter = parameterObject["filter"];
255   if (filter.isMember("genreid"))
256     videoUrl.AddOption("genreid", (int)filter["genreid"].asInteger());
257   else if (filter.isMember("genre"))
258     videoUrl.AddOption("genre", filter["genre"].asString());
259   else if (filter.isMember("year"))
260     videoUrl.AddOption("year", (int)filter["year"].asInteger());
261   else if (filter.isMember("actor"))
262     videoUrl.AddOption("actor", filter["actor"].asString());
263   else if (filter.isMember("director"))
264     videoUrl.AddOption("director", filter["director"].asString());
265   else if (filter.isObject())
266   {
267     CStdString xsp;
268     if (!GetXspFiltering("episodes", filter, xsp))
269       return InvalidParams;
270
271     videoUrl.AddOption("xsp", xsp);
272   }
273
274   if (tvshowID <= 0 && (season > 0 || videoUrl.HasOption("genreid") || videoUrl.HasOption("genre") || videoUrl.HasOption("actor")))
275     return InvalidParams;
276
277   if (tvshowID > 0)
278   {
279     videoUrl.AddOption("tvshowid", tvshowID);
280     if (season >= 0)
281       videoUrl.AddOption("season", season);
282   }
283
284   CFileItemList items;
285   if (!videodatabase.GetEpisodesByWhere(videoUrl.ToString(), CDatabase::Filter(), items, false, sorting))
286     return InvalidParams;
287
288   return GetAdditionalEpisodeDetails(parameterObject, items, result, videodatabase, false);
289 }
290
291 JSONRPC_STATUS CVideoLibrary::GetEpisodeDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
292 {
293   CVideoDatabase videodatabase;
294   if (!videodatabase.Open())
295     return InternalError;
296
297   int id = (int)parameterObject["episodeid"].asInteger();
298
299   CVideoInfoTag infos;
300   if (!videodatabase.GetEpisodeInfo("", infos, id) || infos.m_iDbId <= 0)
301     return InvalidParams;
302
303   CFileItemPtr pItem = CFileItemPtr(new CFileItem(infos));
304   // We need to set the correct base path to get the valid fanart
305   int tvshowid = infos.m_iIdShow;
306   if (tvshowid <= 0)
307     tvshowid = videodatabase.GetTvShowForEpisode(id);
308
309   CStdString basePath; basePath.Format("videodb://tvshows/titles/%ld/%ld/%ld", tvshowid, infos.m_iSeason, id);
310   pItem->SetPath(basePath);
311
312   HandleFileItem("episodeid", true, "episodedetails", pItem, parameterObject, parameterObject["properties"], result, false);
313   return OK;
314 }
315
316 JSONRPC_STATUS CVideoLibrary::GetMusicVideos(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
317 {
318   CVideoDatabase videodatabase;
319   if (!videodatabase.Open())
320     return InternalError;
321
322   SortDescription sorting;
323   ParseLimits(parameterObject, sorting.limitStart, sorting.limitEnd);
324   if (!ParseSorting(parameterObject, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes))
325     return InvalidParams;
326
327   CVideoDbUrl videoUrl;
328   videoUrl.FromString("videodb://musicvideos/titles/");
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     CStdString 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))
356     return InternalError;
357
358   return GetAdditionalMusicVideoDetails(parameterObject, items, result, videodatabase, false);
359 }
360
361 JSONRPC_STATUS CVideoLibrary::GetMusicVideoDetails(const CStdString &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) || 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
377 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedMovies(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   CFileItemList items;
384   if (!videodatabase.GetRecentlyAddedMoviesNav("videodb://recentlyaddedmovies/", items))
385     return InternalError;
386
387   return GetAdditionalMovieDetails(parameterObject, items, result, videodatabase, true);
388 }
389
390 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedEpisodes(const CStdString &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))
398     return InternalError;
399
400   return GetAdditionalEpisodeDetails(parameterObject, items, result, videodatabase, true);
401 }
402
403 JSONRPC_STATUS CVideoLibrary::GetRecentlyAddedMusicVideos(const CStdString &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))
411     return InternalError;
412
413   return GetAdditionalMusicVideoDetails(parameterObject, items, result, videodatabase, true);
414 }
415
416 JSONRPC_STATUS CVideoLibrary::GetGenres(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
417 {
418   CStdString media = parameterObject["type"].asString();
419   media = media.ToLower();
420   int idContent = -1;
421
422   CStdString strPath = "videodb://";
423   /* select which video content to get genres from*/
424   if (media.Equals("movie"))
425   {
426     idContent = VIDEODB_CONTENT_MOVIES;
427     strPath += "movies";
428   }
429   else if (media.Equals("tvshow"))
430   {
431     idContent = VIDEODB_CONTENT_TVSHOWS;
432     strPath += "tvshows";
433   }
434   else if (media.Equals("musicvideo"))
435   {
436     idContent = VIDEODB_CONTENT_MUSICVIDEOS;
437     strPath += "musicvideos";
438   }
439   strPath += "/genres/";
440  
441   CVideoDatabase videodatabase;
442   if (!videodatabase.Open())
443     return InternalError;
444
445   CFileItemList items;
446   if (!videodatabase.GetGenresNav(strPath, items, idContent))
447     return InternalError;
448
449   /* need to set strTitle in each item*/
450   for (unsigned int i = 0; i < (unsigned int)items.Size(); i++)
451     items[i]->GetVideoInfoTag()->m_strTitle = items[i]->GetLabel();
452
453   HandleFileItemList("genreid", false, "genres", items, parameterObject, result);
454   return OK;
455 }
456
457 JSONRPC_STATUS CVideoLibrary::SetMovieDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
458 {
459   int id = (int)parameterObject["movieid"].asInteger();
460
461   CVideoDatabase videodatabase;
462   if (!videodatabase.Open())
463     return InternalError;
464
465   CVideoInfoTag infos;
466   if (!videodatabase.GetMovieInfo("", infos, id) || infos.m_iDbId <= 0)
467     return InvalidParams;
468
469   // get artwork
470   std::map<std::string, std::string> artwork;
471   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
472
473   int playcount = infos.m_playCount;
474   CDateTime lastPlayed = infos.m_lastPlayed;
475
476   UpdateVideoTag(parameterObject, infos, artwork);
477
478   // we need to manually remove tags/taglinks for now because they aren't replaced
479   // due to scrapers not supporting them
480   videodatabase.RemoveTagsFromItem(id, "movie");
481
482   if (videodatabase.SetDetailsForMovie(infos.m_strFileNameAndPath, infos, artwork, id) <= 0)
483     return InternalError;
484
485   if (playcount != infos.m_playCount || lastPlayed != infos.m_lastPlayed)
486   {
487     // restore original playcount or the new one won't be announced
488     int newPlaycount = infos.m_playCount;
489     infos.m_playCount = playcount;
490     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed.IsValid() ? infos.m_lastPlayed : CDateTime::GetCurrentDateTime());
491   }
492
493   UpdateResumePoint(parameterObject, infos, videodatabase);
494
495   CJSONRPCUtils::NotifyItemUpdated();
496   return ACK;
497 }
498
499 JSONRPC_STATUS CVideoLibrary::SetTVShowDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
500 {
501   int id = (int)parameterObject["tvshowid"].asInteger();
502
503   CVideoDatabase videodatabase;
504   if (!videodatabase.Open())
505     return InternalError;
506
507   CVideoInfoTag infos;
508   if (!videodatabase.GetTvShowInfo("", infos, id) || infos.m_iDbId <= 0)
509     return InvalidParams;
510
511   // get artwork
512   std::map<std::string, std::string> artwork;
513   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
514
515   std::map<int, std::map<std::string, std::string> > seasonArt;
516   videodatabase.GetTvShowSeasonArt(infos.m_iDbId, seasonArt);
517
518   int playcount = infos.m_playCount;
519   CDateTime lastPlayed = infos.m_lastPlayed;
520
521   UpdateVideoTag(parameterObject, infos, artwork);
522
523   // we need to manually remove tags/taglinks for now because they aren't replaced
524   // due to scrapers not supporting them
525   videodatabase.RemoveTagsFromItem(id, "tvshow");
526
527   if (videodatabase.SetDetailsForTvShow(infos.m_strFileNameAndPath, infos, artwork, seasonArt, id) <= 0)
528     return InternalError;
529
530   if (playcount != infos.m_playCount || lastPlayed != infos.m_lastPlayed)
531   {
532     // restore original playcount or the new one won't be announced
533     int newPlaycount = infos.m_playCount;
534     infos.m_playCount = playcount;
535     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed.IsValid() ? infos.m_lastPlayed : CDateTime::GetCurrentDateTime());
536   }
537
538   CJSONRPCUtils::NotifyItemUpdated();
539   return ACK;
540 }
541
542 JSONRPC_STATUS CVideoLibrary::SetEpisodeDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
543 {
544   int id = (int)parameterObject["episodeid"].asInteger();
545
546   CVideoDatabase videodatabase;
547   if (!videodatabase.Open())
548     return InternalError;
549
550   CVideoInfoTag infos;
551   videodatabase.GetEpisodeInfo("", infos, id);
552   if (infos.m_iDbId <= 0)
553   {
554     videodatabase.Close();
555     return InvalidParams;
556   }
557
558   int tvshowid = videodatabase.GetTvShowForEpisode(id);
559   if (tvshowid <= 0)
560   {
561     videodatabase.Close();
562     return InvalidParams;
563   }
564
565   // get artwork
566   std::map<std::string, std::string> artwork;
567   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
568
569   int playcount = infos.m_playCount;
570   CDateTime lastPlayed = infos.m_lastPlayed;
571
572   UpdateVideoTag(parameterObject, infos, artwork);
573
574   if (videodatabase.SetDetailsForEpisode(infos.m_strFileNameAndPath, infos, artwork, tvshowid, id) <= 0)
575     return InternalError;
576
577   if (playcount != infos.m_playCount || lastPlayed != infos.m_lastPlayed)
578   {
579     // restore original playcount or the new one won't be announced
580     int newPlaycount = infos.m_playCount;
581     infos.m_playCount = playcount;
582     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed.IsValid() ? infos.m_lastPlayed : CDateTime::GetCurrentDateTime());
583   }
584
585   UpdateResumePoint(parameterObject, infos, videodatabase);
586
587   CJSONRPCUtils::NotifyItemUpdated();
588   return ACK;
589 }
590
591 JSONRPC_STATUS CVideoLibrary::SetMusicVideoDetails(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
592 {
593   int id = (int)parameterObject["musicvideoid"].asInteger();
594
595   CVideoDatabase videodatabase;
596   if (!videodatabase.Open())
597     return InternalError;
598
599   CVideoInfoTag infos;
600   videodatabase.GetMusicVideoInfo("", infos, id);
601   if (infos.m_iDbId <= 0)
602   {
603     videodatabase.Close();
604     return InvalidParams;
605   }
606
607   // get artwork
608   std::map<std::string, std::string> artwork;
609   videodatabase.GetArtForItem(infos.m_iDbId, infos.m_type, artwork);
610
611   int playcount = infos.m_playCount;
612   CDateTime lastPlayed = infos.m_lastPlayed;
613
614   UpdateVideoTag(parameterObject, infos, artwork);
615
616   // we need to manually remove tags/taglinks for now because they aren't replaced
617   // due to scrapers not supporting them
618   videodatabase.RemoveTagsFromItem(id, "musicvideo");
619
620   if (videodatabase.SetDetailsForMusicVideo(infos.m_strFileNameAndPath, infos, artwork, id) <= 0)
621     return InternalError;
622
623   if (playcount != infos.m_playCount || lastPlayed != infos.m_lastPlayed)
624   {
625     // restore original playcount or the new one won't be announced
626     int newPlaycount = infos.m_playCount;
627     infos.m_playCount = playcount;
628     videodatabase.SetPlayCount(CFileItem(infos), newPlaycount, infos.m_lastPlayed.IsValid() ? infos.m_lastPlayed : CDateTime::GetCurrentDateTime());
629   }
630
631   UpdateResumePoint(parameterObject, infos, videodatabase);
632
633   CJSONRPCUtils::NotifyItemUpdated();
634   return ACK;
635 }
636
637 JSONRPC_STATUS CVideoLibrary::RemoveMovie(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
638 {
639   return RemoveVideo(parameterObject);
640 }
641
642 JSONRPC_STATUS CVideoLibrary::RemoveTVShow(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
643 {
644   return RemoveVideo(parameterObject);
645 }
646
647 JSONRPC_STATUS CVideoLibrary::RemoveEpisode(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
648 {
649   return RemoveVideo(parameterObject);
650 }
651
652 JSONRPC_STATUS CVideoLibrary::RemoveMusicVideo(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
653 {
654   return RemoveVideo(parameterObject);
655 }
656
657 JSONRPC_STATUS CVideoLibrary::Scan(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
658 {
659   std::string directory = parameterObject["directory"].asString();
660   CStdString cmd;
661   if (directory.empty())
662     cmd = "updatelibrary(video)";
663   else
664     cmd.Format("updatelibrary(video, %s)", StringUtils::Paramify(directory).c_str());
665
666   CApplicationMessenger::Get().ExecBuiltIn(cmd);
667   return ACK;
668 }
669
670 JSONRPC_STATUS CVideoLibrary::Export(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
671 {
672   CStdString cmd;
673   if (parameterObject["options"].isMember("path"))
674     cmd.Format("exportlibrary(video, false, %s)", StringUtils::Paramify(parameterObject["options"]["path"].asString()));
675   else
676     cmd.Format("exportlibrary(video, true, %s, %s, %s)",
677       parameterObject["options"]["images"].asBoolean() ? "true" : "false",
678       parameterObject["options"]["overwrite"].asBoolean() ? "true" : "false",
679       parameterObject["options"]["actorthumbs"].asBoolean() ? "true" : "false");
680
681   CApplicationMessenger::Get().ExecBuiltIn(cmd);
682   return ACK;
683 }
684
685 JSONRPC_STATUS CVideoLibrary::Clean(const CStdString &method, ITransportLayer *transport, IClient *client, const CVariant &parameterObject, CVariant &result)
686 {
687   CApplicationMessenger::Get().ExecBuiltIn("cleanlibrary(video)");
688   return ACK;
689 }
690
691 bool CVideoLibrary::FillFileItem(const CStdString &strFilename, CFileItemPtr &item, const CVariant &parameterObject /* = CVariant(CVariant::VariantTypeArray) */)
692 {
693   CVideoDatabase videodatabase;
694   if (strFilename.empty() || !videodatabase.Open())
695     return false;
696
697   CVideoInfoTag details;
698   if (!videodatabase.LoadVideoInfo(strFilename, details))
699     return false;
700
701   item->SetFromVideoInfoTag(details);
702   if (item->GetLabel().empty())
703     item->SetLabel(CUtil::GetTitleFromPath(strFilename, false));
704   if (item->GetLabel())
705     item->SetLabel(URIUtils::GetFileName(strFilename));
706   return true;
707 }
708
709 bool CVideoLibrary::FillFileItemList(const CVariant &parameterObject, CFileItemList &list)
710 {
711   CVideoDatabase videodatabase;
712   if (!videodatabase.Open())
713     return false;
714
715   CStdString file = parameterObject["file"].asString();
716   int movieID = (int)parameterObject["movieid"].asInteger(-1);
717   int episodeID = (int)parameterObject["episodeid"].asInteger(-1);
718   int musicVideoID = (int)parameterObject["musicvideoid"].asInteger(-1);
719
720   bool success = false;
721   CFileItemPtr fileItem(new CFileItem());
722   if (FillFileItem(file, fileItem))
723   {
724     success = true;
725     list.Add(fileItem);
726   }
727
728   if (movieID > 0)
729   {
730     CVideoInfoTag details;
731     videodatabase.GetMovieInfo("", details, movieID);
732     if (!details.IsEmpty())
733     {
734       list.Add(CFileItemPtr(new CFileItem(details)));
735       success = true;
736     }
737   }
738   if (episodeID > 0)
739   {
740     CVideoInfoTag details;
741     if (videodatabase.GetEpisodeInfo("", details, episodeID) && !details.IsEmpty())
742     {
743       list.Add(CFileItemPtr(new CFileItem(details)));
744       success = true;
745     }
746   }
747   if (musicVideoID > 0)
748   {
749     CVideoInfoTag details;
750     videodatabase.GetMusicVideoInfo("", details, musicVideoID);
751     if (!details.IsEmpty())
752     {
753       list.Add(CFileItemPtr(new CFileItem(details)));
754       success = true;
755     }
756   }
757
758   return success;
759 }
760
761 JSONRPC_STATUS CVideoLibrary::GetAdditionalMovieDetails(const CVariant &parameterObject, CFileItemList &items, CVariant &result, CVideoDatabase &videodatabase, bool limit /* = true */)
762 {
763   if (!videodatabase.Open())
764     return InternalError;
765
766   bool additionalInfo = false;
767   for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
768   {
769     CStdString fieldValue = itr->asString();
770     if (fieldValue == "cast" || fieldValue == "showlink" || fieldValue == "tag" || fieldValue == "streamdetails")
771       additionalInfo = true;
772   }
773
774   if (additionalInfo)
775   {
776     for (int index = 0; index < items.Size(); index++)
777     {
778       if (additionalInfo)
779         videodatabase.GetMovieInfo("", *(items[index]->GetVideoInfoTag()), items[index]->GetVideoInfoTag()->m_iDbId);
780     }
781   }
782
783   int size = items.Size();
784   if (!limit && items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
785     size = (int)items.GetProperty("total").asInteger();
786   HandleFileItemList("movieid", true, "movies", items, parameterObject, result, size, limit);
787
788   return OK;
789 }
790
791 JSONRPC_STATUS CVideoLibrary::GetAdditionalEpisodeDetails(const CVariant &parameterObject, CFileItemList &items, CVariant &result, CVideoDatabase &videodatabase, bool limit /* = true */)
792 {
793   if (!videodatabase.Open())
794     return InternalError;
795
796   bool additionalInfo = false;
797   for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
798   {
799     CStdString fieldValue = itr->asString();
800     if (fieldValue == "cast" || fieldValue == "streamdetails")
801       additionalInfo = true;
802   }
803
804   if (additionalInfo)
805   {
806     for (int index = 0; index < items.Size(); index++)
807     {
808       if (additionalInfo)
809         videodatabase.GetEpisodeInfo("", *(items[index]->GetVideoInfoTag()), items[index]->GetVideoInfoTag()->m_iDbId);
810     }
811   }
812   
813   int size = items.Size();
814   if (!limit && items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
815     size = (int)items.GetProperty("total").asInteger();
816   HandleFileItemList("episodeid", true, "episodes", items, parameterObject, result, size, limit);
817
818   return OK;
819 }
820
821 JSONRPC_STATUS CVideoLibrary::GetAdditionalMusicVideoDetails(const CVariant &parameterObject, CFileItemList &items, CVariant &result, CVideoDatabase &videodatabase, bool limit /* = true */)
822 {
823   if (!videodatabase.Open())
824     return InternalError;
825
826   bool streamdetails = false;
827   for (CVariant::const_iterator_array itr = parameterObject["properties"].begin_array(); itr != parameterObject["properties"].end_array(); itr++)
828   {
829     if (itr->asString() == "tag" || itr->asString() == "streamdetails")
830       streamdetails = true;
831   }
832
833   if (streamdetails)
834   {
835     for (int index = 0; index < items.Size(); index++)
836       videodatabase.GetMusicVideoInfo("", *(items[index]->GetVideoInfoTag()), items[index]->GetVideoInfoTag()->m_iDbId);
837   }
838
839   int size = items.Size();
840   if (!limit && items.HasProperty("total") && items.GetProperty("total").asInteger() > size)
841     size = (int)items.GetProperty("total").asInteger();
842   HandleFileItemList("musicvideoid", true, "musicvideos", items, parameterObject, result, size, limit);
843
844   return OK;
845 }
846
847 JSONRPC_STATUS CVideoLibrary::RemoveVideo(const CVariant &parameterObject)
848 {
849   CVideoDatabase videodatabase;
850   if (!videodatabase.Open())
851     return InternalError;
852
853   if (parameterObject.isMember("movieid"))
854     videodatabase.DeleteMovie((int)parameterObject["movieid"].asInteger());
855   else if (parameterObject.isMember("tvshowid"))
856     videodatabase.DeleteTvShow((int)parameterObject["tvshowid"].asInteger());
857   else if (parameterObject.isMember("episodeid"))
858     videodatabase.DeleteEpisode((int)parameterObject["episodeid"].asInteger());
859   else if (parameterObject.isMember("musicvideoid"))
860     videodatabase.DeleteMusicVideo((int)parameterObject["musicvideoid"].asInteger());
861
862   CJSONRPCUtils::NotifyItemUpdated();
863   return ACK;
864 }
865
866 void CVideoLibrary::UpdateResumePoint(const CVariant &parameterObject, CVideoInfoTag &details, CVideoDatabase &videodatabase)
867 {
868   if (!parameterObject["resume"].isNull())
869   {
870     CBookmark bookmark;
871     videodatabase.GetResumeBookMark(details.m_strFileNameAndPath, bookmark);
872     int position = (int)parameterObject["resume"]["position"].asInteger();
873     int total = (int)parameterObject["resume"]["total"].asInteger();
874     if (position == 0)
875       videodatabase.ClearBookMarksOfFile(details.m_strFileNameAndPath, CBookmark::RESUME);
876     else
877     {
878       bookmark.timeInSeconds = position;
879       if (total > 0)
880         bookmark.totalTimeInSeconds = total;
881       videodatabase.AddBookMarkToFile(details.m_strFileNameAndPath, bookmark, CBookmark::RESUME);
882     }
883   }
884 }
885
886 void CVideoLibrary::UpdateVideoTag(const CVariant &parameterObject, CVideoInfoTag& details, std::map<std::string, std::string> &artwork)
887 {
888   if (ParameterNotNull(parameterObject, "title"))
889     details.m_strTitle = parameterObject["title"].asString();
890   if (ParameterNotNull(parameterObject, "playcount"))
891     details.m_playCount = (int)parameterObject["playcount"].asInteger();
892   if (ParameterNotNull(parameterObject, "runtime"))
893     details.m_duration = (int)parameterObject["runtime"].asInteger();
894   if (ParameterNotNull(parameterObject, "director"))
895     CopyStringArray(parameterObject["director"], details.m_director);
896   if (ParameterNotNull(parameterObject, "studio"))
897     CopyStringArray(parameterObject["studio"], details.m_studio);
898   if (ParameterNotNull(parameterObject, "year"))
899     details.m_iYear = (int)parameterObject["year"].asInteger();
900   if (ParameterNotNull(parameterObject, "plot"))
901     details.m_strPlot = parameterObject["plot"].asString();
902   if (ParameterNotNull(parameterObject, "album"))
903     details.m_strAlbum = parameterObject["album"].asString();
904   if (ParameterNotNull(parameterObject, "artist"))
905     CopyStringArray(parameterObject["artist"], details.m_artist);
906   if (ParameterNotNull(parameterObject, "genre"))
907     CopyStringArray(parameterObject["genre"], details.m_genre);
908   if (ParameterNotNull(parameterObject, "track"))
909     details.m_iTrack = (int)parameterObject["track"].asInteger();
910   if (ParameterNotNull(parameterObject, "rating"))
911     details.m_fRating = parameterObject["rating"].asFloat();
912   if (ParameterNotNull(parameterObject, "mpaa"))
913     details.m_strMPAARating = parameterObject["mpaa"].asString();
914   if (ParameterNotNull(parameterObject, "imdbnumber"))
915     details.m_strIMDBNumber = parameterObject["imdbnumber"].asString();
916   if (ParameterNotNull(parameterObject, "premiered"))
917     details.m_premiered.SetFromDBDate(parameterObject["premiered"].asString());
918   if (ParameterNotNull(parameterObject, "votes"))
919     details.m_strVotes = parameterObject["votes"].asString();
920   if (ParameterNotNull(parameterObject, "lastplayed"))
921     details.m_lastPlayed.SetFromDBDateTime(parameterObject["lastplayed"].asString());
922   if (ParameterNotNull(parameterObject, "firstaired"))
923     details.m_firstAired.SetFromDBDateTime(parameterObject["firstaired"].asString());
924   if (ParameterNotNull(parameterObject, "productioncode"))
925     details.m_strProductionCode = parameterObject["productioncode"].asString();
926   if (ParameterNotNull(parameterObject, "season"))
927     details.m_iSeason = (int)parameterObject["season"].asInteger();
928   if (ParameterNotNull(parameterObject, "episode"))
929     details.m_iEpisode = (int)parameterObject["episode"].asInteger();
930   if (ParameterNotNull(parameterObject, "originaltitle"))
931     details.m_strOriginalTitle = parameterObject["originaltitle"].asString();
932   if (ParameterNotNull(parameterObject, "trailer"))
933     details.m_strTrailer = parameterObject["trailer"].asString();
934   if (ParameterNotNull(parameterObject, "tagline"))
935     details.m_strTagLine = parameterObject["tagline"].asString();
936   if (ParameterNotNull(parameterObject, "plotoutline"))
937     details.m_strPlotOutline = parameterObject["plotoutline"].asString();
938   if (ParameterNotNull(parameterObject, "writer"))
939     CopyStringArray(parameterObject["writer"], details.m_writingCredits);
940   if (ParameterNotNull(parameterObject, "country"))
941     CopyStringArray(parameterObject["country"], details.m_country);
942   if (ParameterNotNull(parameterObject, "top250"))
943     details.m_iTop250 = (int)parameterObject["top250"].asInteger();
944   if (ParameterNotNull(parameterObject, "sorttitle"))
945     details.m_strSortTitle = parameterObject["sorttitle"].asString();
946   if (ParameterNotNull(parameterObject, "episodeguide"))
947     details.m_strEpisodeGuide = parameterObject["episodeguide"].asString();
948   if (ParameterNotNull(parameterObject, "set"))
949     details.m_strSet = parameterObject["set"].asString();
950   if (ParameterNotNull(parameterObject, "showlink"))
951     CopyStringArray(parameterObject["showlink"], details.m_showLink);
952   if (ParameterNotNull(parameterObject, "thumbnail"))
953     artwork["thumb"] = parameterObject["thumbnail"].asString();
954   if (ParameterNotNull(parameterObject, "fanart"))
955     artwork["fanart"] = parameterObject["fanart"].asString();
956   if (ParameterNotNull(parameterObject, "tag"))
957     CopyStringArray(parameterObject["tag"], details.m_tags);
958   if (ParameterNotNull(parameterObject, "art"))
959   {
960     CVariant art = parameterObject["art"];
961     for (CVariant::const_iterator_map artIt = art.begin_map(); artIt != art.end_map(); artIt++)
962     {
963       if (!artIt->second.asString().empty())
964         artwork[artIt->first] = CTextureCache::UnwrapImageURL(artIt->second.asString());
965     }
966   }
967 }