Merge pull request #4857 from t-nelson/Gotham_13.2_backports
[vuplus_xbmc] / xbmc / playlists / PlayList.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 "PlayList.h"
22 #include "PlayListFactory.h"
23 #include <sstream>
24 #include "video/VideoInfoTag.h"
25 #include "music/tags/MusicInfoTag.h"
26 #include "filesystem/File.h"
27 #include "utils/log.h"
28 #include "utils/URIUtils.h"
29 #include "utils/Variant.h"
30 #include "interfaces/AnnouncementManager.h"
31
32 //using namespace std;
33 using namespace MUSIC_INFO;
34 using namespace XFILE;
35 using namespace PLAYLIST;
36
37 CPlayList::CPlayList(int id)
38   : m_id(id)
39 {
40   m_strPlayListName = "";
41   m_iPlayableItems = -1;
42   m_bShuffled = false;
43   m_bWasPlayed = false;
44 }
45
46 void CPlayList::AnnounceRemove(int pos)
47 {
48   if (m_id < 0)
49     return;
50
51   CVariant data;
52   data["playlistid"] = m_id;
53   data["position"] = pos;
54   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnRemove", data);
55 }
56
57 void CPlayList::AnnounceClear()
58 {
59   if (m_id < 0)
60     return;
61
62   CVariant data;
63   data["playlistid"] = m_id;
64   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnClear", data);
65 }
66
67 void CPlayList::AnnounceAdd(const CFileItemPtr& item, int pos)
68 {
69   if (m_id < 0)
70     return;
71
72   CVariant data;
73   data["playlistid"] = m_id;
74   data["position"] = pos;
75   ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Playlist, "xbmc", "OnAdd", item, data);
76 }
77
78 void CPlayList::Add(const CFileItemPtr &item, int iPosition, int iOrder)
79 {
80   int iOldSize = size();
81   if (iPosition < 0 || iPosition >= iOldSize)
82     iPosition = iOldSize;
83   if (iOrder < 0 || iOrder >= iOldSize)
84     item->m_iprogramCount = iOldSize;
85   else
86     item->m_iprogramCount = iOrder;
87
88   // videodb files are not supported by the filesystem as yet
89   if (item->IsVideoDb())
90     item->SetPath(item->GetVideoInfoTag()->m_strFileNameAndPath);
91
92   // increment the playable counter
93   item->ClearProperty("unplayable");
94   if (m_iPlayableItems < 0)
95     m_iPlayableItems = 1;
96   else
97     m_iPlayableItems++;
98
99   // set 'IsPlayable' property - needed for properly handling plugin:// URLs
100   item->SetProperty("IsPlayable", true);
101
102   //CLog::Log(LOGDEBUG,"%s item:(%02i/%02i)[%s]", __FUNCTION__, iPosition, item->m_iprogramCount, item->GetPath().c_str());
103   if (iPosition == iOldSize)
104     m_vecItems.push_back(item);
105   else
106   {
107     ivecItems it = m_vecItems.begin() + iPosition;
108     m_vecItems.insert(it, 1, item);
109     // correct any duplicate order values
110     if (iOrder < iOldSize)
111       IncrementOrder(iPosition + 1, iOrder);
112   }
113   AnnounceAdd(item, iPosition);
114 }
115
116 void CPlayList::Add(const CFileItemPtr &item)
117 {
118   Add(item, -1, -1);
119 }
120
121 void CPlayList::Add(CPlayList& playlist)
122 {
123   for (int i = 0; i < (int)playlist.size(); i++)
124     Add(playlist[i], -1, -1);
125 }
126
127 void CPlayList::Add(CFileItemList& items)
128 {
129   for (int i = 0; i < (int)items.Size(); i++)
130     Add(items[i]);
131 }
132
133 void CPlayList::Insert(CPlayList& playlist, int iPosition /* = -1 */)
134 {
135   // out of bounds so just add to the end
136   int iSize = size();
137   if (iPosition < 0 || iPosition >= iSize)
138   {
139     Add(playlist);
140     return;
141   }
142   for (int i = 0; i < (int)playlist.size(); i++)
143   {
144     int iPos = iPosition + i;
145     Add(playlist[i], iPos, iPos);
146   }
147 }
148
149 void CPlayList::Insert(CFileItemList& items, int iPosition /* = -1 */)
150 {
151   // out of bounds so just add to the end
152   int iSize = size();
153   if (iPosition < 0 || iPosition >= iSize)
154   {
155     Add(items);
156     return;
157   }
158   for (int i = 0; i < (int)items.Size(); i++)
159   {
160     Add(items[i], iPosition + i, iPosition + i);
161   }
162 }
163
164 void CPlayList::Insert(const CFileItemPtr &item, int iPosition /* = -1 */)
165 {
166   // out of bounds so just add to the end
167   int iSize = size();
168   if (iPosition < 0 || iPosition >= iSize)
169   {
170     Add(item);
171     return;
172   }
173   Add(item, iPosition, iPosition);
174 }
175
176 void CPlayList::DecrementOrder(int iOrder)
177 {
178   if (iOrder < 0) return;
179
180   // it was the last item so do nothing
181   if (iOrder == size()) return;
182
183   // fix all items with an order greater than the removed iOrder
184   ivecItems it;
185   it = m_vecItems.begin();
186   while (it != m_vecItems.end())
187   {
188     CFileItemPtr item = *it;
189     if (item->m_iprogramCount > iOrder)
190     {
191       //CLog::Log(LOGDEBUG,"%s fixing item at order %i", __FUNCTION__, item->m_iprogramCount);
192       item->m_iprogramCount--;
193     }
194     ++it;
195   }
196 }
197
198 void CPlayList::IncrementOrder(int iPosition, int iOrder)
199 {
200   if (iOrder < 0) return;
201
202   // fix all items with an order equal or greater to the added iOrder at iPos
203   ivecItems it;
204   it = m_vecItems.begin() + iPosition;
205   while (it != m_vecItems.end())
206   {
207     CFileItemPtr item = *it;
208     if (item->m_iprogramCount >= iOrder)
209     {
210       //CLog::Log(LOGDEBUG,"%s fixing item at order %i", __FUNCTION__, item->m_iprogramCount);
211       item->m_iprogramCount++;
212     }
213     ++it;
214   }
215 }
216
217 void CPlayList::Clear()
218 {
219   m_vecItems.erase(m_vecItems.begin(), m_vecItems.end());
220   m_strPlayListName = "";
221   m_iPlayableItems = -1;
222   m_bWasPlayed = false;
223
224   AnnounceClear();
225 }
226
227 int CPlayList::size() const
228 {
229   return (int)m_vecItems.size();
230 }
231
232 const CFileItemPtr CPlayList::operator[] (int iItem) const
233 {
234   if (iItem < 0 || iItem >= size())
235   {
236     assert(false);
237     CLog::Log(LOGERROR, "Error trying to retrieve an item that's out of range");
238     return CFileItemPtr();
239   }
240   return m_vecItems[iItem];
241 }
242
243 CFileItemPtr CPlayList::operator[] (int iItem)
244 {
245   if (iItem < 0 || iItem >= size())
246   {
247     assert(false);
248     CLog::Log(LOGERROR, "Error trying to retrieve an item that's out of range");
249     return CFileItemPtr();
250   }
251   return m_vecItems[iItem];
252 }
253
254 void CPlayList::Shuffle(int iPosition)
255 {
256   if (size() == 0)
257     // nothing to shuffle, just set the flag for later
258     m_bShuffled = true;
259   else
260   {
261     if (iPosition >= size())
262       return;
263     if (iPosition < 0)
264       iPosition = 0;
265     CLog::Log(LOGDEBUG,"%s shuffling at pos:%i", __FUNCTION__, iPosition);
266
267     ivecItems it = m_vecItems.begin() + iPosition;
268     random_shuffle(it, m_vecItems.end());
269
270     // the list is now shuffled!
271     m_bShuffled = true;
272   }
273 }
274
275 struct SSortPlayListItem
276 {
277   static bool PlaylistSort(const CFileItemPtr &left, const CFileItemPtr &right)
278   {
279     return (left->m_iprogramCount <= right->m_iprogramCount);
280   }
281 };
282
283 void CPlayList::UnShuffle()
284 {
285   sort(m_vecItems.begin(), m_vecItems.end(), SSortPlayListItem::PlaylistSort);
286   // the list is now unshuffled!
287   m_bShuffled = false;
288 }
289
290 const CStdString& CPlayList::GetName() const
291 {
292   return m_strPlayListName;
293 }
294
295 void CPlayList::Remove(const CStdString& strFileName)
296 {
297   int iOrder = -1;
298   int position = 0;
299   ivecItems it;
300   it = m_vecItems.begin();
301   while (it != m_vecItems.end() )
302   {
303     CFileItemPtr item = *it;
304     if (item->GetPath() == strFileName)
305     {
306       iOrder = item->m_iprogramCount;
307       it = m_vecItems.erase(it);
308       AnnounceRemove(position);
309       //CLog::Log(LOGDEBUG,"PLAYLIST, removing item at order %i", iPos);
310     }
311     else
312     {
313       ++position;
314       ++it;
315     }
316   }
317   DecrementOrder(iOrder);
318 }
319
320 int CPlayList::FindOrder(int iOrder) const
321 {
322   for (int i = 0; i < size(); i++)
323   {
324     if (m_vecItems[i]->m_iprogramCount == iOrder)
325       return i;
326   }
327   return -1;
328 }
329
330 // remove item from playlist by position
331 void CPlayList::Remove(int position)
332 {
333   int iOrder = -1;
334   if (position >= 0 && position < (int)m_vecItems.size())
335   {
336     iOrder = m_vecItems[position]->m_iprogramCount;
337     m_vecItems.erase(m_vecItems.begin() + position);
338   }
339   DecrementOrder(iOrder);
340
341   AnnounceRemove(position);
342 }
343
344 int CPlayList::RemoveDVDItems()
345 {
346   std::vector <CStdString> vecFilenames;
347
348   // Collect playlist items from DVD share
349   ivecItems it;
350   it = m_vecItems.begin();
351   while (it != m_vecItems.end() )
352   {
353     CFileItemPtr item = *it;
354     if ( item->IsCDDA() || item->IsOnDVD() )
355     {
356       vecFilenames.push_back( item->GetPath() );
357     }
358     it++;
359   }
360
361   // Delete them from playlist
362   int nFileCount = vecFilenames.size();
363   if ( nFileCount )
364   {
365     std::vector <CStdString>::iterator it;
366     it = vecFilenames.begin();
367     while (it != vecFilenames.end() )
368     {
369       CStdString& strFilename = *it;
370       Remove( strFilename );
371       it++;
372     }
373     vecFilenames.erase( vecFilenames.begin(), vecFilenames.end() );
374   }
375   return nFileCount;
376 }
377
378 bool CPlayList::Swap(int position1, int position2)
379 {
380   if (
381     (position1 < 0) ||
382     (position2 < 0) ||
383     (position1 >= size()) ||
384     (position2 >= size())
385   )
386   {
387     return false;
388   }
389
390   if (!IsShuffled())
391   {
392     // swap the ordinals before swapping the items!
393     //CLog::Log(LOGDEBUG,"PLAYLIST swapping items at orders (%i, %i)",m_vecItems[position1]->m_iprogramCount,m_vecItems[position2]->m_iprogramCount);
394     std::swap(m_vecItems[position1]->m_iprogramCount, m_vecItems[position2]->m_iprogramCount);
395   }
396
397   // swap the items
398   std::swap(m_vecItems[position1], m_vecItems[position2]);
399   return true;
400 }
401
402 void CPlayList::SetUnPlayable(int iItem)
403 {
404   if (iItem < 0 || iItem >= size())
405   {
406     CLog::Log(LOGWARNING, "Attempt to set unplayable index %d", iItem);
407     return;
408   }
409
410   CFileItemPtr item = m_vecItems[iItem];
411   if (!item->GetProperty("unplayable").asBoolean())
412   {
413     item->SetProperty("unplayable", true);
414     m_iPlayableItems--;
415   }
416 }
417
418
419 bool CPlayList::Load(const CStdString& strFileName)
420 {
421   Clear();
422   m_strBasePath = URIUtils::GetDirectory(strFileName);
423
424   CFileStream file;
425   if (!file.Open(strFileName))
426     return false;
427
428   if (file.GetLength() > 1024*1024)
429   {
430     CLog::Log(LOGWARNING, "%s - File is larger than 1 MB, most likely not a playlist", __FUNCTION__);
431     return false;
432   }
433
434   return LoadData(file);
435 }
436
437 bool CPlayList::LoadData(std::istream &stream)
438 {
439   // try to read as a string
440   std::ostringstream ostr;
441   ostr << stream.rdbuf();
442   return LoadData(ostr.str());
443 }
444
445 bool CPlayList::LoadData(const CStdString& strData)
446 {
447   return false;
448 }
449
450
451 bool CPlayList::Expand(int position)
452 {
453   CFileItemPtr item = m_vecItems[position];
454   std::auto_ptr<CPlayList> playlist (CPlayListFactory::Create(*item.get()));
455   if ( NULL == playlist.get())
456     return false;
457
458   if(!playlist->Load(item->GetPath()))
459     return false;
460
461   // remove any item that points back to itself
462   for(int i = 0;i<playlist->size();i++)
463   {
464     if( (*playlist)[i]->GetPath().Equals( item->GetPath() ) )
465     {
466       playlist->Remove(i);
467       i--;
468     }
469   }
470
471   if(playlist->size() <= 0)
472     return false;
473
474   Remove(position);
475   Insert(*playlist, position);
476   return true;
477 }
478
479 void CPlayList::UpdateItem(const CFileItem *item)
480 {
481   if (!item) return;
482
483   for (ivecItems it = m_vecItems.begin(); it != m_vecItems.end(); ++it)
484   {
485     CFileItemPtr playlistItem = *it;
486     if (playlistItem->IsSamePath(item))
487     {
488       CStdString temp = playlistItem->GetPath(); // save path, it may have been altered
489       *playlistItem = *item;
490       playlistItem->SetPath(temp);
491       break;
492     }
493   }
494 }
495
496 const CStdString& CPlayList::ResolveURL(const CFileItemPtr &item ) const
497 {
498   if (item->IsMusicDb() && item->HasMusicInfoTag())
499     return item->GetMusicInfoTag()->GetURL();
500   else
501     return item->GetPath();
502 }