2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
12 #include "FileItemList.h"
13 #include "PlayListFactory.h"
14 #include "ServiceBroker.h"
15 #include "filesystem/File.h"
16 #include "interfaces/AnnouncementManager.h"
17 #include "music/MusicFileItemClassify.h"
18 #include "music/tags/MusicInfoTag.h"
19 #include "utils/Random.h"
20 #include "utils/StringUtils.h"
21 #include "utils/URIUtils.h"
22 #include "utils/Variant.h"
23 #include "utils/log.h"
33 using namespace MUSIC_INFO
;
34 using namespace XFILE
;
36 namespace KODI::PLAYLIST
39 CPlayList::CPlayList(Id id
/* = PLAYLIST::TYPE_NONE */) : m_id(id
)
41 m_iPlayableItems
= -1;
46 void CPlayList::AnnounceRemove(int pos
)
48 if (m_id
== Id::TYPE_NONE
)
52 data
["playlistid"] = static_cast<int>(m_id
);
53 data
["position"] = pos
;
54 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist
, "OnRemove", data
);
57 void CPlayList::AnnounceClear()
59 if (m_id
== Id::TYPE_NONE
)
63 data
["playlistid"] = static_cast<int>(m_id
);
64 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist
, "OnClear", data
);
67 void CPlayList::AnnounceAdd(const std::shared_ptr
<CFileItem
>& item
, int pos
)
69 if (m_id
== Id::TYPE_NONE
)
73 data
["playlistid"] = static_cast<int>(m_id
);
74 data
["position"] = pos
;
75 CServiceBroker::GetAnnouncementManager()->Announce(ANNOUNCEMENT::Playlist
, "OnAdd", item
, data
);
78 void CPlayList::Add(const std::shared_ptr
<CFileItem
>& item
, int iPosition
, int iOrder
)
80 int iOldSize
= size();
81 if (iPosition
< 0 || iPosition
>= iOldSize
)
83 if (iOrder
< 0 || iOrder
>= iOldSize
)
84 item
->m_iprogramCount
= iOldSize
;
86 item
->m_iprogramCount
= iOrder
;
88 // increment the playable counter
89 item
->ClearProperty("unplayable");
90 if (m_iPlayableItems
< 0)
95 // set 'IsPlayable' property - needed for properly handling plugin:// URLs
96 item
->SetProperty("IsPlayable", true);
98 //CLog::Log(LOGDEBUG,"{} item:({:02}/{:02})[{}]", __FUNCTION__, iPosition, item->m_iprogramCount, item->GetPath());
99 if (iPosition
== iOldSize
)
100 m_vecItems
.push_back(item
);
103 ivecItems it
= m_vecItems
.begin() + iPosition
;
104 m_vecItems
.insert(it
, 1, item
);
105 // correct any duplicate order values
106 if (iOrder
< iOldSize
)
107 IncrementOrder(iPosition
+ 1, iOrder
);
109 AnnounceAdd(item
, iPosition
);
112 void CPlayList::Add(const std::shared_ptr
<CFileItem
>& item
)
117 void CPlayList::Add(const CPlayList
& playlist
)
119 for (int i
= 0; i
< playlist
.size(); i
++)
120 Add(playlist
[i
], -1, -1);
123 void CPlayList::Add(const CFileItemList
& items
)
125 for (int i
= 0; i
< items
.Size(); i
++)
129 void CPlayList::Insert(const CPlayList
& playlist
, int iPosition
/* = -1 */)
131 // out of bounds so just add to the end
133 if (iPosition
< 0 || iPosition
>= iSize
)
138 for (int i
= 0; i
< playlist
.size(); i
++)
140 int iPos
= iPosition
+ i
;
141 Add(playlist
[i
], iPos
, iPos
);
145 void CPlayList::Insert(const CFileItemList
& items
, int iPosition
/* = -1 */)
147 // out of bounds so just add to the end
149 if (iPosition
< 0 || iPosition
>= iSize
)
154 for (int i
= 0; i
< items
.Size(); i
++)
156 Add(items
[i
], iPosition
+ i
, iPosition
+ i
);
160 void CPlayList::Insert(const std::shared_ptr
<CFileItem
>& item
, int iPosition
/* = -1 */)
162 // out of bounds so just add to the end
164 if (iPosition
< 0 || iPosition
>= iSize
)
169 Add(item
, iPosition
, iPosition
);
172 void CPlayList::DecrementOrder(int iOrder
)
174 if (iOrder
< 0) return;
176 // it was the last item so do nothing
177 if (iOrder
== size()) return;
179 // fix all items with an order greater than the removed iOrder
181 it
= m_vecItems
.begin();
182 while (it
!= m_vecItems
.end())
184 CFileItemPtr item
= *it
;
185 if (item
->m_iprogramCount
> iOrder
)
187 //CLog::Log(LOGDEBUG,"{} fixing item at order {}", __FUNCTION__, item->m_iprogramCount);
188 item
->m_iprogramCount
--;
194 void CPlayList::IncrementOrder(int iPosition
, int iOrder
)
196 if (iOrder
< 0) return;
198 // fix all items with an order equal or greater to the added iOrder at iPos
200 it
= m_vecItems
.begin() + iPosition
;
201 while (it
!= m_vecItems
.end())
203 CFileItemPtr item
= *it
;
204 if (item
->m_iprogramCount
>= iOrder
)
206 //CLog::Log(LOGDEBUG,"{} fixing item at order {}", __FUNCTION__, item->m_iprogramCount);
207 item
->m_iprogramCount
++;
213 void CPlayList::Clear()
215 bool announce
= false;
216 if (!m_vecItems
.empty())
218 m_vecItems
.erase(m_vecItems
.begin(), m_vecItems
.end());
221 m_strPlayListName
= "";
222 m_iPlayableItems
= -1;
223 m_bWasPlayed
= false;
229 int CPlayList::size() const
231 return (int)m_vecItems
.size();
234 const std::shared_ptr
<CFileItem
> CPlayList::operator[](int iItem
) const
236 if (iItem
< 0 || iItem
>= size())
239 CLog::Log(LOGERROR
, "Error trying to retrieve an item that's out of range");
240 return CFileItemPtr();
242 return m_vecItems
[iItem
];
245 std::shared_ptr
<CFileItem
> CPlayList::operator[](int iItem
)
247 if (iItem
< 0 || iItem
>= size())
250 CLog::Log(LOGERROR
, "Error trying to retrieve an item that's out of range");
251 return CFileItemPtr();
253 return m_vecItems
[iItem
];
256 void CPlayList::Shuffle(int iPosition
)
259 // nothing to shuffle, just set the flag for later
263 if (iPosition
>= size())
267 CLog::Log(LOGDEBUG
, "{} shuffling at pos:{}", __FUNCTION__
, iPosition
);
269 ivecItems it
= m_vecItems
.begin() + iPosition
;
270 KODI::UTILS::RandomShuffle(it
, m_vecItems
.end());
272 // the list is now shuffled!
277 struct SSortPlayListItem
279 static bool PlaylistSort(const CFileItemPtr
&left
, const CFileItemPtr
&right
)
281 return (left
->m_iprogramCount
< right
->m_iprogramCount
);
285 void CPlayList::UnShuffle()
287 std::sort(m_vecItems
.begin(), m_vecItems
.end(), SSortPlayListItem::PlaylistSort
);
288 // the list is now unshuffled!
292 const std::string
& CPlayList::GetName() const
294 return m_strPlayListName
;
297 void CPlayList::Remove(const std::string
& strFileName
)
302 it
= m_vecItems
.begin();
303 while (it
!= m_vecItems
.end() )
305 CFileItemPtr item
= *it
;
306 if (item
->GetPath() == strFileName
)
308 iOrder
= item
->m_iprogramCount
;
309 it
= m_vecItems
.erase(it
);
310 AnnounceRemove(position
);
311 //CLog::Log(LOGDEBUG,"PLAYLIST, removing item at order {}", iPos);
319 DecrementOrder(iOrder
);
322 int CPlayList::FindOrder(int iOrder
) const
324 for (int i
= 0; i
< size(); i
++)
326 if (m_vecItems
[i
]->m_iprogramCount
== iOrder
)
332 // remove item from playlist by position
333 void CPlayList::Remove(int position
)
336 if (position
>= 0 && position
< (int)m_vecItems
.size())
338 iOrder
= m_vecItems
[position
]->m_iprogramCount
;
339 m_vecItems
.erase(m_vecItems
.begin() + position
);
341 DecrementOrder(iOrder
);
343 AnnounceRemove(position
);
346 int CPlayList::RemoveDVDItems()
348 std::vector
<std::string
> vecFilenames
;
350 // Collect playlist items from DVD share
352 it
= m_vecItems
.begin();
353 while (it
!= m_vecItems
.end() )
355 CFileItemPtr item
= *it
;
356 if (MUSIC::IsCDDA(*item
) || item
->IsOnDVD())
358 vecFilenames
.push_back( item
->GetPath() );
363 // Delete them from playlist
364 int nFileCount
= vecFilenames
.size();
367 std::vector
<std::string
>::iterator it
;
368 it
= vecFilenames
.begin();
369 while (it
!= vecFilenames
.end() )
371 std::string
& strFilename
= *it
;
372 Remove( strFilename
);
375 vecFilenames
.erase( vecFilenames
.begin(), vecFilenames
.end() );
380 bool CPlayList::Swap(int position1
, int position2
)
385 (position1
>= size()) ||
386 (position2
>= size())
394 // swap the ordinals before swapping the items!
395 //CLog::Log(LOGDEBUG,"PLAYLIST swapping items at orders ({}, {})",m_vecItems[position1]->m_iprogramCount,m_vecItems[position2]->m_iprogramCount);
396 std::swap(m_vecItems
[position1
]->m_iprogramCount
, m_vecItems
[position2
]->m_iprogramCount
);
400 std::swap(m_vecItems
[position1
], m_vecItems
[position2
]);
404 void CPlayList::SetUnPlayable(int iItem
)
406 if (iItem
< 0 || iItem
>= size())
408 CLog::Log(LOGWARNING
, "Attempt to set unplayable index {}", iItem
);
412 CFileItemPtr item
= m_vecItems
[iItem
];
413 if (!item
->GetProperty("unplayable").asBoolean())
415 item
->SetProperty("unplayable", true);
421 bool CPlayList::Load(const std::string
& strFileName
)
424 m_strBasePath
= URIUtils::GetDirectory(strFileName
);
427 if (!file
.Open(strFileName
))
430 if (file
.GetLength() > 1024*1024)
432 CLog::Log(LOGWARNING
, "{} - File is larger than 1 MB, most likely not a playlist",
437 return LoadData(file
);
440 bool CPlayList::LoadData(std::istream
&stream
)
442 // try to read as a string
443 std::ostringstream ostr
;
444 ostr
<< stream
.rdbuf();
445 return LoadData(ostr
.str());
448 bool CPlayList::LoadData(const std::string
& strData
)
454 bool CPlayList::Expand(int position
)
456 CFileItemPtr item
= m_vecItems
[position
];
457 std::unique_ptr
<CPlayList
> playlist (CPlayListFactory::Create(*item
.get()));
458 if (playlist
== nullptr)
461 std::string path
= item
->GetDynPath();
463 if (!playlist
->Load(path
))
466 // remove any item that points back to itself
467 for (int i
= 0;i
<playlist
->size();i
++)
469 if (StringUtils::EqualsNoCase((*playlist
)[i
]->GetPath(), path
))
477 // never change original path (id) of a file item
478 for (int i
= 0;i
<playlist
->size();i
++)
480 (*playlist
)[i
]->SetDynPath((*playlist
)[i
]->GetPath());
481 (*playlist
)[i
]->SetPath(item
->GetDynPath());
482 (*playlist
)[i
]->SetStartOffset(item
->GetStartOffset());
485 if (playlist
->size() <= 0)
489 Insert(*playlist
, position
);
493 void CPlayList::UpdateItem(const CFileItem
*item
)
497 for (ivecItems it
= m_vecItems
.begin(); it
!= m_vecItems
.end(); ++it
)
499 CFileItemPtr playlistItem
= *it
;
500 if (playlistItem
->IsSamePath(item
))
502 std::string temp
= playlistItem
->GetPath(); // save path, it may have been altered
503 *playlistItem
= *item
;
504 playlistItem
->SetPath(temp
);
510 const std::string
& CPlayList::ResolveURL(const std::shared_ptr
<CFileItem
>& item
) const
512 if (MUSIC::IsMusicDb(*item
) && item
->HasMusicInfoTag())
513 return item
->GetMusicInfoTag()->GetURL();
515 return item
->GetDynPath();
518 } // namespace KODI::PLAYLIST