[XBOX] Merged: Linuxport revisions
[xbmc:xbmc-antiquated.git] / xbmc / FileSystem / CMythDirectory.cpp
1 /*
2  *      Copyright (C) 2005-2008 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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "stdafx.h"
23 #include "CMythDirectory.h"
24 #include "CMythSession.h"
25 #include "Util.h"
26 #include "DllLibCMyth.h"
27 #include "VideoInfoTag.h"
28 #include "URL.h"
29 #include "GUISettings.h"
30 #include "Settings.h"
31 #include "FileItem.h"
32
33 extern "C" {
34 #include "lib/libcmyth/cmyth.h"
35 #include "lib/libcmyth/mvp_refmem.h"
36 }
37
38 using namespace DIRECTORY;
39 using namespace XFILE;
40 using namespace std;
41
42 CCMythDirectory::CCMythDirectory()
43 {
44   m_session  = NULL;
45   m_dll      = NULL;
46   m_database = NULL;
47   m_recorder = NULL;
48 }
49
50 CCMythDirectory::~CCMythDirectory()
51 {
52   Release();
53 }
54
55 void CCMythDirectory::Release()
56 {
57   if(m_recorder)
58   {
59     m_dll->ref_release(m_recorder);
60     m_recorder = NULL;
61   }
62   if(m_session)
63   {
64     CCMythSession::ReleaseSession(m_session);
65     m_session = NULL;
66   }
67   m_dll = NULL;
68 }
69
70 bool CCMythDirectory::GetGuide(const CStdString& base, CFileItemList &items)
71 {
72   cmyth_database_t db = m_session->GetDatabase();
73   if(!db)
74     return false;
75
76   cmyth_chanlist_t list = m_dll->mysql_get_chanlist(db);
77   if(!list)
78   {
79     CLog::Log(LOGERROR, "%s - unable to get list of channels with url %s", __FUNCTION__, base.c_str());
80     return false;
81   }
82   CURL url(base);
83
84   int count = m_dll->chanlist_get_count(list);
85   for(int i = 0; i < count; i++)
86   {
87     cmyth_channel_t channel = m_dll->chanlist_get_item(list, i);
88     if(channel)
89     {
90       CStdString name, path, icon;
91
92       if(!m_dll->channel_visible(channel))
93       {
94         m_dll->ref_release(channel);
95         continue;
96       }
97       int num = m_dll->channel_channum(channel);
98       char* str;
99       if((str = m_dll->channel_name(channel)))
100       {
101         name.Format("%d - %s", num, str);
102         m_dll->ref_release(str);
103       }
104       else
105         name.Format("%d");
106
107       icon = GetValue(m_dll->channel_icon(channel));
108
109       if(num <= 0)
110       {
111         CLog::Log(LOGDEBUG, "%s - Channel '%s' Icon '%s' - Skipped", __FUNCTION__, name.c_str(), icon.c_str());
112       }
113       else
114       {
115         CLog::Log(LOGDEBUG, "%s - Channel '%s' Icon '%s'", __FUNCTION__, name.c_str(), icon.c_str());
116         path.Format("guide/%d/", num);
117         url.SetFileName(path);
118         url.GetURL(path);
119         CFileItemPtr item(new CFileItem(path, true));
120         item->SetLabel(name);
121         item->SetLabelPreformated(true);
122         if(icon.length() > 0)
123         {
124           url.SetFileName("files/channels/" + CUtil::GetFileName(icon));
125           url.GetURL(icon);
126           item->SetThumbnailImage(icon);
127         }
128         items.Add(item);
129       }
130       m_dll->ref_release(channel);
131     }
132   }
133   m_dll->ref_release(list);
134   return true;
135 }
136
137 bool CCMythDirectory::GetGuideForChannel(const CStdString& base, CFileItemList &items, int channelNumber)
138 {
139   cmyth_database_t db = m_session->GetDatabase();
140   if(!db)
141   {
142     CLog::Log(LOGERROR, "%s - Could not get database", __FUNCTION__);
143     return false;
144   }
145
146   time_t now;
147   time(&now);
148   // this sets how many seconds of EPG from now we should grab
149   time_t end = now + (1 * 24 * 60 * 60);
150
151   cmyth_program_t *prog = NULL;
152
153   int count = m_dll->mysql_get_guide(db, &prog, now, end);
154   CLog::Log(LOGDEBUG, "%s - %i entries of guide data", __FUNCTION__, count);
155   if (count <= 0)
156     return false;
157
158   for (int i=0; i < count; i++)
159   {
160     if(prog[i].channum == channelNumber)
161     {
162       CStdString path;
163       path.Format("%s%s", base.c_str(), prog[i].title);
164
165       CDateTime starttime(prog[i].starttime);
166       CDateTime endtime(prog[i].endtime);
167
168       CStdString title;
169       title.Format("%s - \"%s\"", starttime.GetAsLocalizedDateTime(), prog[i].title);
170
171       CFileItemPtr item(new CFileItem(title, false));
172       item->SetLabel(title);
173       item->m_dateTime = starttime;
174       item->SetLabelPreformated(true);
175
176       CVideoInfoTag* tag = item->GetVideoInfoTag();
177
178       tag->m_strAlbum       = GetValue(prog[i].callsign);
179       tag->m_strShowTitle   = GetValue(prog[i].title);
180       tag->m_strPlotOutline = GetValue(prog[i].subtitle);
181       tag->m_strPlot        = GetValue(prog[i].description);
182       tag->m_strGenre       = GetValue(prog[i].category);
183
184       if(tag->m_strPlot.Left(tag->m_strPlotOutline.length()) != tag->m_strPlotOutline && !tag->m_strPlotOutline.IsEmpty())
185           tag->m_strPlot = tag->m_strPlotOutline + '\n' + tag->m_strPlot;
186       tag->m_strOriginalTitle = tag->m_strShowTitle;
187
188       tag->m_strTitle = tag->m_strAlbum;
189       if(tag->m_strShowTitle.length() > 0)
190         tag->m_strTitle += " : " + tag->m_strShowTitle;
191
192       CDateTimeSpan runtime = endtime - starttime;
193       StringUtils::SecondsToTimeString( runtime.GetSeconds()
194                                       + runtime.GetMinutes() * 60
195                                       + runtime.GetHours() * 3600, tag->m_strRuntime);
196
197       tag->m_iSeason  = 0; /* set this so xbmc knows it's a tv show */
198       tag->m_iEpisode = 0;
199       tag->m_strStatus = prog[i].rec_status;
200       items.Add(item);
201     }
202   }
203   m_dll->ref_release(prog);
204   return true;
205 }
206
207 bool CCMythDirectory::GetRecordings(const CStdString& base, CFileItemList &items, enum FilterType type, const CStdString& filter)
208 {
209   cmyth_conn_t control = m_session->GetControl();
210   if(!control)
211     return false;
212
213   cmyth_proglist_t list = m_dll->proglist_get_all_recorded(control);
214   if(!list)
215   {
216     CLog::Log(LOGERROR, "%s - unable to get list of recordings", __FUNCTION__);
217     return false;
218   }
219
220   int count = m_dll->proglist_get_count(list);
221   for(int i = 0; i<count; i++)
222   {
223     cmyth_proginfo_t program = m_dll->proglist_get_item(list, i);
224     if(program)
225     {
226       if(GetValue(m_dll->proginfo_recgroup(program)).Equals("LiveTV"))
227       {
228         m_dll->ref_release(program);
229         continue;
230       }
231
232       CURL url(base);
233       CStdString path = CUtil::GetFileName(GetValue(m_dll->proginfo_pathname(program)));
234       CStdString name = GetValue(m_dll->proginfo_title(program));
235       
236       switch (type)
237       {
238         case MOVIES:
239         if (!IsMovie(program))
240         {
241           m_dll->ref_release(program);
242           continue;
243         }
244         url.SetFileName("movies/" + path);
245         break;
246         case TV_SHOWS:
247         if (filter != name)
248         {
249           m_dll->ref_release(program);
250           continue;
251         }
252         url.SetFileName("tvshows/" + path);
253         break;
254         case NONE:
255         url.SetFileName("recordings/" + path);
256         break;
257       }
258
259       CFileItemPtr item(new CFileItem("", false));
260       m_session->UpdateItem(*item, program);
261       url.GetURL(item->m_strPath);
262
263       url.SetFileName("files/" + path +  ".png");
264       url.GetURL(path);
265       item->SetThumbnailImage(path);
266
267       /*
268        * Don't adjust the name for MOVIES as additional information in the name will affect any scraper lookup.
269        */
270       if (type != MOVIES)
271       {
272         if (m_dll->proginfo_rec_status(program) == RS_RECORDING)
273         {
274           name += " (Recording)";
275           item->SetThumbnailImage("");
276         }
277         else
278           name += " (" + item->m_dateTime.GetAsLocalizedDateTime() + ")";
279       }
280
281       item->SetLabel(name);
282       item->SetLabelPreformated(true);
283
284       items.Add(item);
285       m_dll->ref_release(program);
286     }
287
288     if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
289       items.AddSortMethod(SORT_METHOD_LABEL_IGNORE_THE, 551, LABEL_MASKS("%Z (%J)", "%I", "%L", ""));
290     else
291       items.AddSortMethod(SORT_METHOD_LABEL, 551, LABEL_MASKS("%Z (%J)", "%I", "%L", ""));
292     items.AddSortMethod(SORT_METHOD_SIZE, 553, LABEL_MASKS("%Z (%J)", "%I", "%L", "%I"));
293     items.AddSortMethod(SORT_METHOD_DATE, 552, LABEL_MASKS("%Z", "%J %Q", "%L", "%J"));
294
295   }
296   m_dll->ref_release(list);
297   return true;
298 }
299
300 /**
301  * \brief Gets a list of folders for recordings based on what filter is requested
302  *
303  */
304 bool CCMythDirectory::GetRecordingFolders(const CStdString& base, CFileItemList &items, const enum FilterType type)
305 {
306   cmyth_conn_t control = m_session->GetControl();
307   if(!control)
308     return false;
309
310   cmyth_proglist_t list = m_dll->proglist_get_all_recorded(control);
311   if(!list)
312   {
313     CLog::Log(LOGERROR, "%s - unable to get list of recordings", __FUNCTION__);
314     return false;
315   }
316
317   int count = m_dll->proglist_get_count(list);
318   for(int i=0; i<count; i++)
319   {
320     cmyth_proginfo_t program = m_dll->proglist_get_item(list, i);
321     if(program)
322     {
323       if(GetValue(m_dll->proginfo_recgroup(program)).Equals("LiveTV"))
324       {
325         m_dll->ref_release(program);
326         continue;
327       }
328
329       CStdString itemName = "";
330       if(type == TV_SHOWS && IsTvShow(program))
331         itemName = GetValue(m_dll->proginfo_title(program));
332       else
333       {
334         // MOVIES and NONE don't use folder groupings.
335         m_dll->ref_release(program);
336         continue;
337       }
338
339       // Don't add repeats of a virtual directory
340       if (items.Contains(base + "/" + itemName + "/"))
341       {
342         m_dll->ref_release(program);
343         continue;
344       }
345
346       CFileItemPtr item(new CFileItem(base + "/" + itemName + "/", true));
347       item->SetLabel(itemName);
348       item->SetLabelPreformated(true);
349
350       items.Add(item);
351       m_dll->ref_release(program);
352     }
353
354     if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
355       items.AddSortMethod(SORT_METHOD_LABEL_IGNORE_THE, 551, LABEL_MASKS("%Z (%J)", "%I", "%L", ""));
356     else
357       items.AddSortMethod(SORT_METHOD_LABEL, 551, LABEL_MASKS("%Z (%J)", "%I", "%L", ""));
358   }
359   m_dll->ref_release(list);
360   return true;
361 }
362
363 bool CCMythDirectory::GetChannels(const CStdString& base, CFileItemList &items)
364 {
365   cmyth_conn_t control = m_session->GetControl();
366   if(!control)
367     return false;
368
369   vector<cmyth_proginfo_t> channels;
370   for(unsigned i=0;i<16;i++)
371   {
372     cmyth_recorder_t recorder = m_dll->conn_get_recorder_from_num(control, i);
373     if(!recorder)
374       continue;
375
376     cmyth_proginfo_t program;
377     program = m_dll->recorder_get_cur_proginfo(recorder);
378     program = m_dll->recorder_get_next_proginfo(recorder, program, BROWSE_DIRECTION_UP);
379     if(!program) {
380       m_dll->ref_release(m_recorder);
381       continue;
382     }
383
384     long startchan = m_dll->proginfo_chan_id(program);
385     long currchan  = -1;
386     while(startchan != currchan)
387     {
388       unsigned j;
389       for(j=0;j<channels.size();j++)
390       {
391         if(m_dll->proginfo_compare(program, channels[j]) == 0)
392           break;
393       }
394
395       if(j == channels.size())
396         channels.push_back(program);
397
398       program = m_dll->recorder_get_next_proginfo(recorder, program, BROWSE_DIRECTION_UP);
399       if(!program)
400         break;
401
402       currchan = m_dll->proginfo_chan_id(program);
403     }
404     m_dll->ref_release(recorder);
405   }
406
407   CURL url(base);
408
409   for(unsigned i=0;i<channels.size();i++)
410   {
411     cmyth_proginfo_t program = channels[i];
412     CStdString num, progname, channame, icon, sign;
413
414     num      = GetValue(m_dll->proginfo_chanstr (program));
415     icon     = GetValue(m_dll->proginfo_chanicon(program));
416
417     CFileItemPtr item(new CFileItem("", false));
418     m_session->UpdateItem(*item, program);
419     url.SetFileName("channels/" + num + ".ts");
420     url.GetURL(item->m_strPath);
421     item->SetLabel(GetValue(m_dll->proginfo_chansign(program)));
422
423     if(icon.length() > 0)
424     {
425       url.SetFileName("files/channels/" + CUtil::GetFileName(icon));
426       url.GetURL(icon);
427       item->SetThumbnailImage(icon);
428     }
429
430     /* hack to get sorting working properly when sorting by show title */
431     if(item->GetVideoInfoTag()->m_strShowTitle.IsEmpty())
432       item->GetVideoInfoTag()->m_strShowTitle = " ";
433
434     items.Add(item);
435     m_dll->ref_release(program);
436   }
437
438   if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
439     items.AddSortMethod(SORT_METHOD_LABEL_IGNORE_THE, 551, LABEL_MASKS("%K[ - %B]", "%Z", "%L", ""));
440   else
441     items.AddSortMethod(SORT_METHOD_LABEL, 551, LABEL_MASKS("%K[ - %B]", "%Z", "%L", ""));
442
443   if (g_guiSettings.GetBool("filelists.ignorethewhensorting"))
444     items.AddSortMethod(SORT_METHOD_LABEL_IGNORE_THE, 20364, LABEL_MASKS("%Z", "%B", "%L", ""));
445   else
446     items.AddSortMethod(SORT_METHOD_LABEL, 20364, LABEL_MASKS("%Z", "%B", "%L", ""));
447
448
449   return true;
450 }
451
452 bool CCMythDirectory::GetDirectory(const CStdString& strPath, CFileItemList &items)
453 {
454   m_session = CCMythSession::AquireSession(strPath);
455   if(!m_session)
456     return false;
457
458   m_dll = m_session->GetLibrary();
459   if(!m_dll)
460     return false;
461
462   CStdString base(strPath);
463   CUtil::RemoveSlashAtEnd(base);
464
465   CURL url(strPath);
466   CStdString fileName = url.GetFileName();
467   CUtil::RemoveSlashAtEnd(fileName);
468
469   if (fileName == "")
470   {
471     CFileItemPtr item;
472
473     item.reset(new CFileItem(base + "/channels/", true));
474     item->SetLabel(g_localizeStrings.Get(22018)); // Live channels
475     item->SetLabelPreformated(true);
476     items.Add(item);
477
478     item.reset(new CFileItem(base + "/guide/", true));
479     item->SetLabel(g_localizeStrings.Get(22020)); // Guide
480     item->SetLabelPreformated(true);
481     items.Add(item);
482
483     item.reset(new CFileItem(base + "/movies/", true));
484     item->SetLabel(g_localizeStrings.Get(20342)); // Movies
485     item->SetLabelPreformated(true);
486     items.Add(item);
487
488     item.reset(new CFileItem(base + "/recordings/", true));
489     item->SetLabel(g_localizeStrings.Get(22015)); // All recordings
490     item->SetLabelPreformated(true);
491     items.Add(item);
492
493     item.reset(new CFileItem(base + "/tvshows/", true));
494     item->SetLabel(g_localizeStrings.Get(20343)); // TV shows
495     item->SetLabelPreformated(true);
496     items.Add(item);
497
498     return true;
499   }
500   else if (fileName == "channels")
501     return GetChannels(base, items);
502   else if (fileName == "guide")
503     return GetGuide(base, items);
504   else if (fileName.Left(6) == "guide/")
505     return GetGuideForChannel(base, items, atoi(fileName.Right(fileName.length() - 6)));
506   else if (fileName == "movies")
507     return GetRecordings(base, items, MOVIES);
508   else if (fileName == "recordings")
509     return GetRecordings(base, items);
510   else if (fileName == "tvshows")
511     return GetRecordingFolders(base, items, TV_SHOWS);
512   else if (fileName.Left(8) == "tvshows/")
513     return GetRecordings(base, items, TV_SHOWS, fileName.Right(fileName.length() - 8));
514   return false;
515 }
516
517 CDateTime CCMythDirectory::GetValue(cmyth_timestamp_t t)
518 {
519   return m_session->GetValue(t);
520 }
521
522 bool CCMythDirectory::IsMovie(const cmyth_proginfo_t program)
523 {
524   /*
525    * The mythconverg.recordedprogram.programid field (if it exists) is a combination key where the first 2 characters map
526    * to the category_type and the rest is the key. From MythTV/release-0-21-fixes/mythtv/libs/libmythtv/programinfo.cpp
527    * "MV" = movie
528    * "EP" = series
529    * "SP" = sports
530    * "SH" = tvshow
531    *
532    * Based on MythTV usage it appears that the programid is only filled in for Movies though. Shame, could have used
533    * it for the other categories as well.
534    *
535    * mythconverg.recordedprogram.category_type contains the exact information that is needed. However, category_type
536    * isn't available through the libcmyth API. Since there is a direct correlation between the programid starting
537    * with "MV" and the category_type being "movie" that should work fine.
538    */
539
540   const int iMovieLength = g_advancedSettings.m_iMythMovieLength; // Minutes
541   if (iMovieLength > 0) // Use hack to identify movie based on length (used if EPG is dubious).
542     return GetValue(m_dll->proginfo_programid(program)).Left(2) == "MV"
543     ||     m_dll->proginfo_length_sec(program) > iMovieLength * 60; // Minutes to seconds
544   else
545     return GetValue(m_dll->proginfo_programid(program)).Left(2) == "MV";
546 }
547
548 bool CCMythDirectory::IsTvShow(const cmyth_proginfo_t program)
549 {
550   /*
551    * There isn't enough information exposed by libcmyth to distinguish between an episode in a series and a
552    * one off TV show. See comment in IsMovie for more information.
553    *
554    * Anything that isn't a movie is considered a TV show.
555    */
556   return !IsMovie(program);
557 }