changed: Add logic to properly handle subtitles for stacked files
[vuplus_xbmc] / xbmc / pvr / channels / PVRChannelGroupInternal.cpp
1 /*
2  *      Copyright (C) 2012-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 "PVRChannelGroupInternal.h"
22
23 #include "guilib/GUIWindowManager.h"
24 #include "dialogs/GUIDialogYesNo.h"
25 #include "dialogs/GUIDialogOK.h"
26 #include "settings/AdvancedSettings.h"
27 #include "utils/log.h"
28
29 #include "PVRChannelGroupsContainer.h"
30 #include "pvr/PVRDatabase.h"
31 #include "pvr/PVRManager.h"
32 #include "epg/EpgContainer.h"
33 #include "pvr/timers/PVRTimers.h"
34 #include "pvr/addons/PVRClients.h"
35
36 using namespace PVR;
37 using namespace EPG;
38 using namespace std;
39
40 CPVRChannelGroupInternal::CPVRChannelGroupInternal(bool bRadio) :
41   CPVRChannelGroup(bRadio, bRadio ? XBMC_INTERNAL_GROUP_RADIO : XBMC_INTERNAL_GROUP_TV, g_localizeStrings.Get(bRadio ? 19216 : 19217))
42 {
43   m_iHiddenChannels = 0;
44   m_iGroupType      = PVR_GROUP_TYPE_INTERNAL;
45 }
46
47 CPVRChannelGroupInternal::CPVRChannelGroupInternal(const CPVRChannelGroup &group) :
48     CPVRChannelGroup(group)
49 {
50   m_iHiddenChannels = group.GetNumHiddenChannels();
51 }
52
53 CPVRChannelGroupInternal::~CPVRChannelGroupInternal(void)
54 {
55   Unload();
56 }
57
58 bool CPVRChannelGroupInternal::Load(void)
59 {
60   if (CPVRChannelGroup::Load())
61   {
62     UpdateChannelPaths();
63     g_PVRManager.TriggerEpgsCreate();
64     return true;
65   }
66
67   CLog::Log(LOGERROR, "PVRChannelGroupInternal - %s - failed to load channels", __FUNCTION__);
68   return false;
69 }
70
71 void CPVRChannelGroupInternal::CheckGroupName(void)
72 {
73   CSingleLock lock(m_critSection);
74
75   /* check whether the group name is still correct, or channels will fail to load after the language setting changed */
76   CStdString strNewGroupName = g_localizeStrings.Get(m_bRadio ? 19216 : 19217);
77   if (!m_strGroupName.Equals(strNewGroupName))
78   {
79     SetGroupName(strNewGroupName, true);
80     UpdateChannelPaths();
81   }
82 }
83
84 void CPVRChannelGroupInternal::UpdateChannelPaths(void)
85 {
86   for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++)
87   {
88     PVRChannelGroupMember member = m_members.at(iChannelPtr);
89     member.channel->UpdatePath(this, iChannelPtr);
90   }
91 }
92
93 void CPVRChannelGroupInternal::UpdateFromClient(const CPVRChannel &channel, unsigned int iChannelNumber /* = 0 */)
94 {
95   CSingleLock lock(m_critSection);
96   CPVRChannelPtr realChannel = GetByClient(channel.UniqueID(), channel.ClientID());
97   if (realChannel)
98     realChannel->UpdateFromClient(channel);
99   else
100   {
101     PVRChannelGroupMember newMember = { CPVRChannelPtr(new CPVRChannel(channel)), iChannelNumber > 0l ? iChannelNumber : (int)m_members.size() + 1 };
102     m_members.push_back(newMember);
103     m_bChanged = true;
104
105     SortAndRenumber();
106   }
107 }
108
109 bool CPVRChannelGroupInternal::InsertInGroup(CPVRChannel &channel, int iChannelNumber /* = 0 */)
110 {
111   CSingleLock lock(m_critSection);
112   return CPVRChannelGroup::AddToGroup(channel, iChannelNumber);
113 }
114
115 bool CPVRChannelGroupInternal::Update(void)
116 {
117   CPVRChannelGroupInternal PVRChannels_tmp(m_bRadio);
118   PVRChannels_tmp.SetPreventSortAndRenumber();
119   return PVRChannels_tmp.LoadFromClients() && UpdateGroupEntries(PVRChannels_tmp);
120 }
121
122 bool CPVRChannelGroupInternal::AddToGroup(CPVRChannel &channel, int iChannelNumber /* = 0 */)
123 {
124   CSingleLock lock(m_critSection);
125
126   bool bReturn(false);
127
128   /* get the actual channel since this is called from a fileitemlist copy */
129   CPVRChannelPtr realChannel = GetByChannelID(channel.ChannelID());
130   if (!realChannel)
131     return bReturn;
132
133   /* switch the hidden flag */
134   if (realChannel->IsHidden())
135   {
136     realChannel->SetHidden(false);
137     m_iHiddenChannels--;
138
139     SortAndRenumber();
140   }
141
142   /* move this channel and persist */
143   bReturn = (iChannelNumber > 0l) ?
144     MoveChannel(realChannel->ChannelNumber(), iChannelNumber, true) :
145     MoveChannel(realChannel->ChannelNumber(), m_members.size() - m_iHiddenChannels, true);
146
147   if (m_bLoaded)
148     realChannel->Persist();
149   return bReturn;
150 }
151
152 bool CPVRChannelGroupInternal::RemoveFromGroup(const CPVRChannel &channel)
153 {
154   CSingleLock lock(m_critSection);
155
156   /* check if this channel is currently playing if we are hiding it */
157   CPVRChannelPtr currentChannel;
158   if (g_PVRManager.GetCurrentChannel(currentChannel) && *currentChannel == channel)
159   {
160     CGUIDialogOK::ShowAndGetInput(19098,19101,0,19102);
161     return false;
162   }
163
164   /* get the actual channel since this is called from a fileitemlist copy */
165   CPVRChannelPtr realChannel = GetByChannelID(channel.ChannelID());
166   if (!realChannel)
167     return false;
168
169   /* switch the hidden flag */
170   if (!realChannel->IsHidden())
171   {
172     realChannel->SetHidden(true);
173     ++m_iHiddenChannels;
174   }
175   else
176   {
177     realChannel->SetHidden(false);
178     --m_iHiddenChannels;
179   }
180
181   /* renumber this list */
182   SortAndRenumber();
183
184   /* and persist */
185   return realChannel->Persist() &&
186       Persist();
187 }
188
189 bool CPVRChannelGroupInternal::MoveChannel(unsigned int iOldChannelNumber, unsigned int iNewChannelNumber, bool bSaveInDb /* = true */)
190 {
191   CSingleLock lock(m_critSection);
192   /* new channel number out of range */
193   if (iNewChannelNumber > m_members.size() - m_iHiddenChannels)
194     iNewChannelNumber = m_members.size() - m_iHiddenChannels;
195
196   return CPVRChannelGroup::MoveChannel(iOldChannelNumber, iNewChannelNumber, bSaveInDb);
197 }
198
199 int CPVRChannelGroupInternal::GetMembers(CFileItemList &results, bool bGroupMembers /* = true */) const
200 {
201   int iOrigSize = results.Size();
202   CSingleLock lock(m_critSection);
203
204   for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++)
205   {
206     CPVRChannelPtr channel = m_members.at(iChannelPtr).channel;
207     if (!channel)
208       continue;
209
210     if (bGroupMembers != channel->IsHidden())
211     {
212       CFileItemPtr pFileItem(new CFileItem(*channel));
213       results.Add(pFileItem);
214     }
215   }
216
217   return results.Size() - iOrigSize;
218 }
219
220 int CPVRChannelGroupInternal::LoadFromDb(bool bCompress /* = false */)
221 {
222   CPVRDatabase *database = GetPVRDatabase();
223   if (!database)
224     return -1;
225
226   int iChannelCount = Size();
227
228   if (database->Get(*this) > 0)
229   {
230     if (bCompress)
231       database->Compress(true);
232   }
233   else
234   {
235     CLog::Log(LOGINFO, "PVRChannelGroupInternal - %s - no channels in the database",
236         __FUNCTION__);
237   }
238
239   SortByChannelNumber();
240
241   return Size() - iChannelCount;
242 }
243
244 bool CPVRChannelGroupInternal::LoadFromClients(void)
245 {
246   /* get the channels from the backends */
247   return g_PVRClients->GetChannels(this) == PVR_ERROR_NO_ERROR;
248 }
249
250 bool CPVRChannelGroupInternal::Renumber(void)
251 {
252   CSingleLock lock(m_critSection);
253   bool bReturn(CPVRChannelGroup::Renumber());
254
255   m_iHiddenChannels = 0;
256   for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size();  iChannelPtr++)
257   {
258     if (m_members.at(iChannelPtr).channel->IsHidden())
259       m_iHiddenChannels++;
260     else
261       m_members.at(iChannelPtr).channel->UpdatePath(this, iChannelPtr);
262   }
263
264   return bReturn;
265 }
266
267 bool CPVRChannelGroupInternal::IsGroupMember(const CPVRChannel &channel) const
268 {
269   return !channel.IsHidden();
270 }
271
272 bool CPVRChannelGroupInternal::UpdateChannel(const CPVRChannel &channel)
273 {
274   CSingleLock lock(m_critSection);
275   CPVRChannelPtr updateChannel = GetByUniqueID(channel.UniqueID());
276
277   if (!updateChannel)
278   {
279     updateChannel = CPVRChannelPtr(new CPVRChannel(channel.IsRadio()));
280     PVRChannelGroupMember newMember = { updateChannel, 0 };
281     m_members.push_back(newMember);
282     updateChannel->SetUniqueID(channel.UniqueID());
283   }
284   updateChannel->UpdateFromClient(channel);
285
286   return updateChannel->Persist(!m_bLoaded);
287 }
288
289 bool CPVRChannelGroupInternal::AddAndUpdateChannels(const CPVRChannelGroup &channels, bool bUseBackendChannelNumbers)
290 {
291   bool bReturn(false);
292   SetPreventSortAndRenumber();
293
294   CSingleLock lock(m_critSection);
295
296   /* go through the channel list and check for updated or new channels */
297   for (unsigned int iChannelPtr = 0; iChannelPtr < channels.m_members.size(); iChannelPtr++)
298   {
299     PVRChannelGroupMember member = channels.m_members.at(iChannelPtr);
300     if (!member.channel)
301       continue;
302
303     /* check whether this channel is present in this container */
304     CPVRChannelPtr existingChannel = GetByClient(member.channel->UniqueID(), member.channel->ClientID());
305     if (existingChannel)
306     {
307       /* if it's present, update the current tag */
308       if (existingChannel->UpdateFromClient(*member.channel))
309       {
310         bReturn = true;
311         CLog::Log(LOGINFO,"PVRChannelGroupInternal - %s - updated %s channel '%s'", __FUNCTION__, m_bRadio ? "radio" : "TV", member.channel->ChannelName().c_str());
312       }
313     }
314     else
315     {
316       /* new channel */
317       UpdateFromClient(*member.channel, bUseBackendChannelNumbers ? member.channel->ClientChannelNumber() : 0);
318       bReturn = true;
319       CLog::Log(LOGINFO,"PVRChannelGroupInternal - %s - added %s channel '%s'", __FUNCTION__, m_bRadio ? "radio" : "TV", member.channel->ChannelName().c_str());
320     }
321   }
322
323   SetPreventSortAndRenumber(false);
324   if (m_bChanged)
325     SortAndRenumber();
326
327   return bReturn;
328 }
329
330 bool CPVRChannelGroupInternal::UpdateGroupEntries(const CPVRChannelGroup &channels)
331 {
332   bool bReturn(false);
333
334   if (CPVRChannelGroup::UpdateGroupEntries(channels))
335   {
336     /* try to find channel icons */
337     if (g_advancedSettings.m_bPVRChannelIconsAutoScan)
338       SearchAndSetChannelIcons();
339
340     g_PVRTimers->UpdateChannels();
341     Persist();
342
343     bReturn = true;
344   }
345
346   return bReturn;
347 }
348
349 void CPVRChannelGroupInternal::CreateChannelEpg(CPVRChannelPtr channel, bool bForce /* = false */)
350 {
351   if (!channel)
352     return;
353
354   CSingleLock lock(channel->m_critSection);
355   if (!channel->m_bEPGCreated || bForce)
356   {
357     CEpg *epg = g_EpgContainer.CreateChannelEpg(channel);
358     if (epg)
359     {
360       channel->m_bEPGCreated = true;
361       if (epg->EpgID() != channel->m_iEpgId)
362       {
363         channel->m_iEpgId = epg->EpgID();
364         channel->m_bChanged = true;
365       }
366     }
367   }
368 }
369
370 bool CPVRChannelGroupInternal::CreateChannelEpgs(bool bForce /* = false */)
371 {
372   if (!g_EpgContainer.IsStarted())
373     return false;
374   {
375     CSingleLock lock(m_critSection);
376     for (unsigned int iChannelPtr = 0; iChannelPtr < m_members.size(); iChannelPtr++)
377       CreateChannelEpg(m_members.at(iChannelPtr).channel);
378   }
379
380   if (HasChangedChannels())
381   {
382     g_EpgContainer.PersistTables();
383     return Persist();
384   }
385
386   return true;
387 }