summaryrefslogtreecommitdiffstats
path: root/xbmc/playlists/PlayList.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/playlists/PlayList.cpp')
-rw-r--r--xbmc/playlists/PlayList.cpp513
1 files changed, 513 insertions, 0 deletions
diff --git a/xbmc/playlists/PlayList.cpp b/xbmc/playlists/PlayList.cpp
new file mode 100644
index 0000000..7c03884
--- /dev/null
+++ b/xbmc/playlists/PlayList.cpp
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "PlayList.h"
+
+#include "FileItem.h"
+#include "PlayListFactory.h"
+#include "ServiceBroker.h"
+#include "filesystem/File.h"
+#include "interfaces/AnnouncementManager.h"
+#include "music/tags/MusicInfoTag.h"
+#include "utils/Random.h"
+#include "utils/StringUtils.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <algorithm>
+#include <cassert>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+
+using namespace MUSIC_INFO;
+using namespace XFILE;
+using namespace PLAYLIST;
+
+CPlayList::CPlayList(Id id /* = PLAYLIST::TYPE_NONE */) : m_id(id)
+{
+ m_iPlayableItems = -1;
+ m_bShuffled = false;
+ m_bWasPlayed = false;
+}
+
+void CPlayList::AnnounceRemove(int pos)
+{
+ if (m_id == TYPE_NONE)
+ return;
+
+ CVariant data;
+ data["playlistid"] = m_id;
+ data["position"] = pos;
+ CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist, "OnRemove", data);
+}
+
+void CPlayList::AnnounceClear()
+{
+ if (m_id == TYPE_NONE)
+ return;
+
+ CVariant data;
+ data["playlistid"] = m_id;
+ CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist, "OnClear", data);
+}
+
+void CPlayList::AnnounceAdd(const std::shared_ptr<CFileItem>& item, int pos)
+{
+ if (m_id == TYPE_NONE)
+ return;
+
+ CVariant data;
+ data["playlistid"] = m_id;
+ data["position"] = pos;
+ CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist, "OnAdd", item, data);
+}
+
+void CPlayList::Add(const std::shared_ptr<CFileItem>& item, int iPosition, int iOrder)
+{
+ int iOldSize = size();
+ if (iPosition < 0 || iPosition >= iOldSize)
+ iPosition = iOldSize;
+ if (iOrder < 0 || iOrder >= iOldSize)
+ item->m_iprogramCount = iOldSize;
+ else
+ item->m_iprogramCount = iOrder;
+
+ // increment the playable counter
+ item->ClearProperty("unplayable");
+ if (m_iPlayableItems < 0)
+ m_iPlayableItems = 1;
+ else
+ m_iPlayableItems++;
+
+ // set 'IsPlayable' property - needed for properly handling plugin:// URLs
+ item->SetProperty("IsPlayable", true);
+
+ //CLog::Log(LOGDEBUG,"{} item:({:02}/{:02})[{}]", __FUNCTION__, iPosition, item->m_iprogramCount, item->GetPath());
+ if (iPosition == iOldSize)
+ m_vecItems.push_back(item);
+ else
+ {
+ ivecItems it = m_vecItems.begin() + iPosition;
+ m_vecItems.insert(it, 1, item);
+ // correct any duplicate order values
+ if (iOrder < iOldSize)
+ IncrementOrder(iPosition + 1, iOrder);
+ }
+ AnnounceAdd(item, iPosition);
+}
+
+void CPlayList::Add(const std::shared_ptr<CFileItem>& item)
+{
+ Add(item, -1, -1);
+}
+
+void CPlayList::Add(const CPlayList& playlist)
+{
+ for (int i = 0; i < playlist.size(); i++)
+ Add(playlist[i], -1, -1);
+}
+
+void CPlayList::Add(const CFileItemList& items)
+{
+ for (int i = 0; i < items.Size(); i++)
+ Add(items[i]);
+}
+
+void CPlayList::Insert(const CPlayList& playlist, int iPosition /* = -1 */)
+{
+ // out of bounds so just add to the end
+ int iSize = size();
+ if (iPosition < 0 || iPosition >= iSize)
+ {
+ Add(playlist);
+ return;
+ }
+ for (int i = 0; i < playlist.size(); i++)
+ {
+ int iPos = iPosition + i;
+ Add(playlist[i], iPos, iPos);
+ }
+}
+
+void CPlayList::Insert(const CFileItemList& items, int iPosition /* = -1 */)
+{
+ // out of bounds so just add to the end
+ int iSize = size();
+ if (iPosition < 0 || iPosition >= iSize)
+ {
+ Add(items);
+ return;
+ }
+ for (int i = 0; i < items.Size(); i++)
+ {
+ Add(items[i], iPosition + i, iPosition + i);
+ }
+}
+
+void CPlayList::Insert(const std::shared_ptr<CFileItem>& item, int iPosition /* = -1 */)
+{
+ // out of bounds so just add to the end
+ int iSize = size();
+ if (iPosition < 0 || iPosition >= iSize)
+ {
+ Add(item);
+ return;
+ }
+ Add(item, iPosition, iPosition);
+}
+
+void CPlayList::DecrementOrder(int iOrder)
+{
+ if (iOrder < 0) return;
+
+ // it was the last item so do nothing
+ if (iOrder == size()) return;
+
+ // fix all items with an order greater than the removed iOrder
+ ivecItems it;
+ it = m_vecItems.begin();
+ while (it != m_vecItems.end())
+ {
+ CFileItemPtr item = *it;
+ if (item->m_iprogramCount > iOrder)
+ {
+ //CLog::Log(LOGDEBUG,"{} fixing item at order {}", __FUNCTION__, item->m_iprogramCount);
+ item->m_iprogramCount--;
+ }
+ ++it;
+ }
+}
+
+void CPlayList::IncrementOrder(int iPosition, int iOrder)
+{
+ if (iOrder < 0) return;
+
+ // fix all items with an order equal or greater to the added iOrder at iPos
+ ivecItems it;
+ it = m_vecItems.begin() + iPosition;
+ while (it != m_vecItems.end())
+ {
+ CFileItemPtr item = *it;
+ if (item->m_iprogramCount >= iOrder)
+ {
+ //CLog::Log(LOGDEBUG,"{} fixing item at order {}", __FUNCTION__, item->m_iprogramCount);
+ item->m_iprogramCount++;
+ }
+ ++it;
+ }
+}
+
+void CPlayList::Clear()
+{
+ bool announce = false;
+ if (!m_vecItems.empty())
+ {
+ m_vecItems.erase(m_vecItems.begin(), m_vecItems.end());
+ announce = true;
+ }
+ m_strPlayListName = "";
+ m_iPlayableItems = -1;
+ m_bWasPlayed = false;
+
+ if (announce)
+ AnnounceClear();
+}
+
+int CPlayList::size() const
+{
+ return (int)m_vecItems.size();
+}
+
+const std::shared_ptr<CFileItem> CPlayList::operator[](int iItem) const
+{
+ if (iItem < 0 || iItem >= size())
+ {
+ assert(false);
+ CLog::Log(LOGERROR, "Error trying to retrieve an item that's out of range");
+ return CFileItemPtr();
+ }
+ return m_vecItems[iItem];
+}
+
+std::shared_ptr<CFileItem> CPlayList::operator[](int iItem)
+{
+ if (iItem < 0 || iItem >= size())
+ {
+ assert(false);
+ CLog::Log(LOGERROR, "Error trying to retrieve an item that's out of range");
+ return CFileItemPtr();
+ }
+ return m_vecItems[iItem];
+}
+
+void CPlayList::Shuffle(int iPosition)
+{
+ if (size() == 0)
+ // nothing to shuffle, just set the flag for later
+ m_bShuffled = true;
+ else
+ {
+ if (iPosition >= size())
+ return;
+ if (iPosition < 0)
+ iPosition = 0;
+ CLog::Log(LOGDEBUG, "{} shuffling at pos:{}", __FUNCTION__, iPosition);
+
+ ivecItems it = m_vecItems.begin() + iPosition;
+ KODI::UTILS::RandomShuffle(it, m_vecItems.end());
+
+ // the list is now shuffled!
+ m_bShuffled = true;
+ }
+}
+
+struct SSortPlayListItem
+{
+ static bool PlaylistSort(const CFileItemPtr &left, const CFileItemPtr &right)
+ {
+ return (left->m_iprogramCount < right->m_iprogramCount);
+ }
+};
+
+void CPlayList::UnShuffle()
+{
+ std::sort(m_vecItems.begin(), m_vecItems.end(), SSortPlayListItem::PlaylistSort);
+ // the list is now unshuffled!
+ m_bShuffled = false;
+}
+
+const std::string& CPlayList::GetName() const
+{
+ return m_strPlayListName;
+}
+
+void CPlayList::Remove(const std::string& strFileName)
+{
+ int iOrder = -1;
+ int position = 0;
+ ivecItems it;
+ it = m_vecItems.begin();
+ while (it != m_vecItems.end() )
+ {
+ CFileItemPtr item = *it;
+ if (item->GetPath() == strFileName)
+ {
+ iOrder = item->m_iprogramCount;
+ it = m_vecItems.erase(it);
+ AnnounceRemove(position);
+ //CLog::Log(LOGDEBUG,"PLAYLIST, removing item at order {}", iPos);
+ }
+ else
+ {
+ ++position;
+ ++it;
+ }
+ }
+ DecrementOrder(iOrder);
+}
+
+int CPlayList::FindOrder(int iOrder) const
+{
+ for (int i = 0; i < size(); i++)
+ {
+ if (m_vecItems[i]->m_iprogramCount == iOrder)
+ return i;
+ }
+ return -1;
+}
+
+// remove item from playlist by position
+void CPlayList::Remove(int position)
+{
+ int iOrder = -1;
+ if (position >= 0 && position < (int)m_vecItems.size())
+ {
+ iOrder = m_vecItems[position]->m_iprogramCount;
+ m_vecItems.erase(m_vecItems.begin() + position);
+ }
+ DecrementOrder(iOrder);
+
+ AnnounceRemove(position);
+}
+
+int CPlayList::RemoveDVDItems()
+{
+ std::vector <std::string> vecFilenames;
+
+ // Collect playlist items from DVD share
+ ivecItems it;
+ it = m_vecItems.begin();
+ while (it != m_vecItems.end() )
+ {
+ CFileItemPtr item = *it;
+ if ( item->IsCDDA() || item->IsOnDVD() )
+ {
+ vecFilenames.push_back( item->GetPath() );
+ }
+ ++it;
+ }
+
+ // Delete them from playlist
+ int nFileCount = vecFilenames.size();
+ if ( nFileCount )
+ {
+ std::vector <std::string>::iterator it;
+ it = vecFilenames.begin();
+ while (it != vecFilenames.end() )
+ {
+ std::string& strFilename = *it;
+ Remove( strFilename );
+ ++it;
+ }
+ vecFilenames.erase( vecFilenames.begin(), vecFilenames.end() );
+ }
+ return nFileCount;
+}
+
+bool CPlayList::Swap(int position1, int position2)
+{
+ if (
+ (position1 < 0) ||
+ (position2 < 0) ||
+ (position1 >= size()) ||
+ (position2 >= size())
+ )
+ {
+ return false;
+ }
+
+ if (!IsShuffled())
+ {
+ // swap the ordinals before swapping the items!
+ //CLog::Log(LOGDEBUG,"PLAYLIST swapping items at orders ({}, {})",m_vecItems[position1]->m_iprogramCount,m_vecItems[position2]->m_iprogramCount);
+ std::swap(m_vecItems[position1]->m_iprogramCount, m_vecItems[position2]->m_iprogramCount);
+ }
+
+ // swap the items
+ std::swap(m_vecItems[position1], m_vecItems[position2]);
+ return true;
+}
+
+void CPlayList::SetUnPlayable(int iItem)
+{
+ if (iItem < 0 || iItem >= size())
+ {
+ CLog::Log(LOGWARNING, "Attempt to set unplayable index {}", iItem);
+ return;
+ }
+
+ CFileItemPtr item = m_vecItems[iItem];
+ if (!item->GetProperty("unplayable").asBoolean())
+ {
+ item->SetProperty("unplayable", true);
+ m_iPlayableItems--;
+ }
+}
+
+
+bool CPlayList::Load(const std::string& strFileName)
+{
+ Clear();
+ m_strBasePath = URIUtils::GetDirectory(strFileName);
+
+ CFileStream file;
+ if (!file.Open(strFileName))
+ return false;
+
+ if (file.GetLength() > 1024*1024)
+ {
+ CLog::Log(LOGWARNING, "{} - File is larger than 1 MB, most likely not a playlist",
+ __FUNCTION__);
+ return false;
+ }
+
+ return LoadData(file);
+}
+
+bool CPlayList::LoadData(std::istream &stream)
+{
+ // try to read as a string
+ std::ostringstream ostr;
+ ostr << stream.rdbuf();
+ return LoadData(ostr.str());
+}
+
+bool CPlayList::LoadData(const std::string& strData)
+{
+ return false;
+}
+
+
+bool CPlayList::Expand(int position)
+{
+ CFileItemPtr item = m_vecItems[position];
+ std::unique_ptr<CPlayList> playlist (CPlayListFactory::Create(*item.get()));
+ if (playlist == nullptr)
+ return false;
+
+ std::string path = item->GetDynPath();
+
+ if (!playlist->Load(path))
+ return false;
+
+ // remove any item that points back to itself
+ for (int i = 0;i<playlist->size();i++)
+ {
+ if (StringUtils::EqualsNoCase((*playlist)[i]->GetPath(), path))
+ {
+ playlist->Remove(i);
+ i--;
+ }
+ }
+
+ // @todo
+ // never change original path (id) of a file item
+ for (int i = 0;i<playlist->size();i++)
+ {
+ (*playlist)[i]->SetDynPath((*playlist)[i]->GetPath());
+ (*playlist)[i]->SetPath(item->GetPath());
+ (*playlist)[i]->SetStartOffset(item->GetStartOffset());
+ }
+
+ if (playlist->size() <= 0)
+ return false;
+
+ Remove(position);
+ Insert(*playlist, position);
+ return true;
+}
+
+void CPlayList::UpdateItem(const CFileItem *item)
+{
+ if (!item) return;
+
+ for (ivecItems it = m_vecItems.begin(); it != m_vecItems.end(); ++it)
+ {
+ CFileItemPtr playlistItem = *it;
+ if (playlistItem->IsSamePath(item))
+ {
+ std::string temp = playlistItem->GetPath(); // save path, it may have been altered
+ *playlistItem = *item;
+ playlistItem->SetPath(temp);
+ break;
+ }
+ }
+}
+
+const std::string& CPlayList::ResolveURL(const std::shared_ptr<CFileItem>& item) const
+{
+ if (item->IsMusicDb() && item->HasMusicInfoTag())
+ return item->GetMusicInfoTag()->GetURL();
+ else
+ return item->GetDynPath();
+}