1 /*
2 * Playlist.cpp - Media Player for the Haiku Operating System
3 *
4 * Copyright (C) 2006 Marcus Overhagen <marcus@overhagen.de>
5 * Copyright (C) 2007-2009 Stephan Aßmus <superstippi@gmx.de> (MIT ok)
6 * Copyright (C) 2008-2009 Fredrik Modéen <[FirstName]@[LastName].se> (MIT ok)
7 *
8 * Released under the terms of the MIT license.
9 */
10
11
12 #include "Playlist.h"
13
14 #include <debugger.h>
15 #include <new>
16 #include <stdio.h>
17 #include <strings.h>
18
19 #include <AppFileInfo.h>
20 #include <Application.h>
21 #include <Autolock.h>
22 #include <Directory.h>
23 #include <Entry.h>
24 #include <File.h>
25 #include <Message.h>
26 #include <Mime.h>
27 #include <NodeInfo.h>
28 #include <Path.h>
29 #include <Roster.h>
30 #include <String.h>
31
32 #include <QueryFile.h>
33
34 #include "FilePlaylistItem.h"
35 #include "FileReadWrite.h"
36 #include "MainApp.h"
37
38 using std::nothrow;
39
40 // TODO: using BList for objects is bad, replace it with a template
41
Listener()42 Playlist::Listener::Listener() {}
~Listener()43 Playlist::Listener::~Listener() {}
ItemAdded(PlaylistItem * item,int32 index)44 void Playlist::Listener::ItemAdded(PlaylistItem* item, int32 index) {}
ItemRemoved(int32 index)45 void Playlist::Listener::ItemRemoved(int32 index) {}
ItemsSorted()46 void Playlist::Listener::ItemsSorted() {}
CurrentItemChanged(int32 newIndex,bool play)47 void Playlist::Listener::CurrentItemChanged(int32 newIndex, bool play) {}
ImportFailed()48 void Playlist::Listener::ImportFailed() {}
49
50
51 // #pragma mark -
52
53
54 static void
make_item_compare_string(const PlaylistItem * item,char * buffer,size_t bufferSize)55 make_item_compare_string(const PlaylistItem* item, char* buffer,
56 size_t bufferSize)
57 {
58 // TODO: Maybe "location" would be useful here as well.
59 // snprintf(buffer, bufferSize, "%s - %s - %0*ld - %s",
60 // item->Author().String(),
61 // item->Album().String(),
62 // 3, item->TrackNumber(),
63 // item->Title().String());
64 snprintf(buffer, bufferSize, "%s", item->LocationURI().String());
65 }
66
67
68 static int
playlist_item_compare(const void * _item1,const void * _item2)69 playlist_item_compare(const void* _item1, const void* _item2)
70 {
71 // compare complete path
72 const PlaylistItem* item1 = *(const PlaylistItem**)_item1;
73 const PlaylistItem* item2 = *(const PlaylistItem**)_item2;
74
75 static const size_t bufferSize = 1024;
76 char string1[bufferSize];
77 make_item_compare_string(item1, string1, bufferSize);
78 char string2[bufferSize];
79 make_item_compare_string(item2, string2, bufferSize);
80
81 return strcmp(string1, string2);
82 }
83
84
85 // #pragma mark -
86
87
Playlist()88 Playlist::Playlist()
89 :
90 BLocker("playlist lock"),
91 fItems(),
92 fCurrentIndex(-1)
93 {
94 }
95
96
~Playlist()97 Playlist::~Playlist()
98 {
99 MakeEmpty();
100
101 if (fListeners.CountItems() > 0)
102 debugger("Playlist::~Playlist() - there are still listeners attached!");
103 }
104
105
106 // #pragma mark - archiving
107
108
109 static const char* kItemArchiveKey = "item";
110
111
112 status_t
Unarchive(const BMessage * archive)113 Playlist::Unarchive(const BMessage* archive)
114 {
115 if (archive == NULL)
116 return B_BAD_VALUE;
117
118 MakeEmpty();
119
120 BMessage itemArchive;
121 for (int32 i = 0;
122 archive->FindMessage(kItemArchiveKey, i, &itemArchive) == B_OK; i++) {
123
124 BArchivable* archivable = instantiate_object(&itemArchive);
125 PlaylistItem* item = dynamic_cast<PlaylistItem*>(archivable);
126 if (!item) {
127 delete archivable;
128 continue;
129 }
130
131 if (!AddItem(item)) {
132 delete item;
133 return B_NO_MEMORY;
134 }
135 }
136
137 return B_OK;
138 }
139
140
141 status_t
Archive(BMessage * into) const142 Playlist::Archive(BMessage* into) const
143 {
144 if (into == NULL)
145 return B_BAD_VALUE;
146
147 int32 count = CountItems();
148 for (int32 i = 0; i < count; i++) {
149 const PlaylistItem* item = ItemAtFast(i);
150 BMessage itemArchive;
151 status_t ret = item->Archive(&itemArchive);
152 if (ret != B_OK)
153 return ret;
154 ret = into->AddMessage(kItemArchiveKey, &itemArchive);
155 if (ret != B_OK)
156 return ret;
157 }
158
159 return B_OK;
160 }
161
162
163 const uint32 kPlaylistMagicBytes = 'MPPL';
164 const char* kTextPlaylistMimeString = "text/x-playlist";
165 const char* kBinaryPlaylistMimeString = "application/x-vnd.haiku-playlist";
166
167 status_t
Unflatten(BDataIO * stream)168 Playlist::Unflatten(BDataIO* stream)
169 {
170 if (stream == NULL)
171 return B_BAD_VALUE;
172
173 uint32 magicBytes;
174 ssize_t read = stream->Read(&magicBytes, 4);
175 if (read != 4) {
176 if (read < 0)
177 return (status_t)read;
178 return B_IO_ERROR;
179 }
180
181 if (B_LENDIAN_TO_HOST_INT32(magicBytes) != kPlaylistMagicBytes)
182 return B_BAD_VALUE;
183
184 BMessage archive;
185 status_t ret = archive.Unflatten(stream);
186 if (ret != B_OK)
187 return ret;
188
189 return Unarchive(&archive);
190 }
191
192
193 status_t
Flatten(BDataIO * stream) const194 Playlist::Flatten(BDataIO* stream) const
195 {
196 if (stream == NULL)
197 return B_BAD_VALUE;
198
199 BMessage archive;
200 status_t ret = Archive(&archive);
201 if (ret != B_OK)
202 return ret;
203
204 uint32 magicBytes = B_HOST_TO_LENDIAN_INT32(kPlaylistMagicBytes);
205 ssize_t written = stream->Write(&magicBytes, 4);
206 if (written != 4) {
207 if (written < 0)
208 return (status_t)written;
209 return B_IO_ERROR;
210 }
211
212 return archive.Flatten(stream);
213 }
214
215
216 // #pragma mark - list access
217
218
219 void
MakeEmpty(bool deleteItems)220 Playlist::MakeEmpty(bool deleteItems)
221 {
222 int32 count = CountItems();
223 for (int32 i = count - 1; i >= 0; i--) {
224 PlaylistItem* item = RemoveItem(i, false);
225 _NotifyItemRemoved(i);
226 if (deleteItems)
227 item->ReleaseReference();
228 }
229 SetCurrentItemIndex(-1);
230 }
231
232
233 int32
CountItems() const234 Playlist::CountItems() const
235 {
236 return fItems.CountItems();
237 }
238
239
240 bool
IsEmpty() const241 Playlist::IsEmpty() const
242 {
243 return fItems.IsEmpty();
244 }
245
246
247 void
Sort()248 Playlist::Sort()
249 {
250 fItems.SortItems(playlist_item_compare);
251 _NotifyItemsSorted();
252 }
253
254
255 bool
AddItem(PlaylistItem * item)256 Playlist::AddItem(PlaylistItem* item)
257 {
258 return AddItem(item, CountItems());
259 }
260
261
262 bool
AddItem(PlaylistItem * item,int32 index)263 Playlist::AddItem(PlaylistItem* item, int32 index)
264 {
265 if (!fItems.AddItem(item, index))
266 return false;
267
268 if (index <= fCurrentIndex)
269 SetCurrentItemIndex(fCurrentIndex + 1, false);
270
271 _NotifyItemAdded(item, index);
272
273 return true;
274 }
275
276
277 bool
AdoptPlaylist(Playlist & other)278 Playlist::AdoptPlaylist(Playlist& other)
279 {
280 return AdoptPlaylist(other, CountItems());
281 }
282
283
284 bool
AdoptPlaylist(Playlist & other,int32 index)285 Playlist::AdoptPlaylist(Playlist& other, int32 index)
286 {
287 if (&other == this)
288 return false;
289 // NOTE: this is not intended to merge two "equal" playlists
290 // the given playlist is assumed to be a temporary "dummy"
291 if (fItems.AddList(&other.fItems, index)) {
292 // take care of the notifications
293 int32 count = other.CountItems();
294 for (int32 i = index; i < index + count; i++) {
295 PlaylistItem* item = ItemAtFast(i);
296 _NotifyItemAdded(item, i);
297 }
298 if (index <= fCurrentIndex)
299 SetCurrentItemIndex(fCurrentIndex + count);
300 // empty the other list, so that the PlaylistItems are now ours
301 other.fItems.MakeEmpty();
302 return true;
303 }
304 return false;
305 }
306
307
308 PlaylistItem*
RemoveItem(int32 index,bool careAboutCurrentIndex)309 Playlist::RemoveItem(int32 index, bool careAboutCurrentIndex)
310 {
311 PlaylistItem* item = (PlaylistItem*)fItems.RemoveItem(index);
312 if (!item)
313 return NULL;
314 _NotifyItemRemoved(index);
315
316 if (careAboutCurrentIndex) {
317 // fCurrentIndex isn't in sync yet, so might be one too large (if the
318 // removed item was above the currently playing item).
319 if (index < fCurrentIndex)
320 SetCurrentItemIndex(fCurrentIndex - 1, false);
321 else if (index == fCurrentIndex) {
322 if (fCurrentIndex == CountItems())
323 fCurrentIndex--;
324 SetCurrentItemIndex(fCurrentIndex, true);
325 }
326 }
327
328 return item;
329 }
330
331
332 int32
IndexOf(PlaylistItem * item) const333 Playlist::IndexOf(PlaylistItem* item) const
334 {
335 return fItems.IndexOf(item);
336 }
337
338
339 PlaylistItem*
ItemAt(int32 index) const340 Playlist::ItemAt(int32 index) const
341 {
342 return (PlaylistItem*)fItems.ItemAt(index);
343 }
344
345
346 PlaylistItem*
ItemAtFast(int32 index) const347 Playlist::ItemAtFast(int32 index) const
348 {
349 return (PlaylistItem*)fItems.ItemAtFast(index);
350 }
351
352
353 // #pragma mark - navigation
354
355
356 bool
SetCurrentItemIndex(int32 index,bool notify)357 Playlist::SetCurrentItemIndex(int32 index, bool notify)
358 {
359 bool result = true;
360 if (index >= CountItems()) {
361 index = CountItems() - 1;
362 result = false;
363 notify = false;
364 }
365 if (index < 0) {
366 index = -1;
367 result = false;
368 }
369 if (index == fCurrentIndex && !notify)
370 return result;
371
372 fCurrentIndex = index;
373 _NotifyCurrentItemChanged(fCurrentIndex, notify);
374 return result;
375 }
376
377
378 int32
CurrentItemIndex() const379 Playlist::CurrentItemIndex() const
380 {
381 return fCurrentIndex;
382 }
383
384
385 void
GetSkipInfo(bool * canSkipPrevious,bool * canSkipNext) const386 Playlist::GetSkipInfo(bool* canSkipPrevious, bool* canSkipNext) const
387 {
388 if (canSkipPrevious)
389 *canSkipPrevious = fCurrentIndex > 0;
390 if (canSkipNext)
391 *canSkipNext = fCurrentIndex < CountItems() - 1;
392 }
393
394
395 // pragma mark -
396
397
398 bool
AddListener(Listener * listener)399 Playlist::AddListener(Listener* listener)
400 {
401 BAutolock _(this);
402 if (listener && !fListeners.HasItem(listener))
403 return fListeners.AddItem(listener);
404 return false;
405 }
406
407
408 void
RemoveListener(Listener * listener)409 Playlist::RemoveListener(Listener* listener)
410 {
411 BAutolock _(this);
412 fListeners.RemoveItem(listener);
413 }
414
415
416 // #pragma mark - support
417
418
419 void
AppendItems(const BMessage * refsReceivedMessage,int32 appendIndex,bool sortItems)420 Playlist::AppendItems(const BMessage* refsReceivedMessage, int32 appendIndex,
421 bool sortItems)
422 {
423 // the playlist is replaced by the refs in the message
424 // or the refs are appended at the appendIndex
425 // in the existing playlist
426 if (appendIndex == APPEND_INDEX_APPEND_LAST)
427 appendIndex = CountItems();
428
429 bool add = appendIndex != APPEND_INDEX_REPLACE_PLAYLIST;
430
431 if (!add)
432 MakeEmpty();
433
434 bool startPlaying = CountItems() == 0;
435
436 Playlist temporaryPlaylist;
437 Playlist* playlist = add ? &temporaryPlaylist : this;
438 bool hasSavedPlaylist = false;
439
440 // TODO: This is not very fair, we should abstract from
441 // entry ref representation and support more URLs.
442 BMessage archivedUrl;
443 if (refsReceivedMessage->FindMessage("mediaplayer:url", &archivedUrl)
444 == B_OK) {
445 BUrl url(&archivedUrl);
446 AddItem(new UrlPlaylistItem(url));
447 }
448
449 entry_ref ref;
450 int32 subAppendIndex = CountItems();
451 for (int i = 0; refsReceivedMessage->FindRef("refs", i, &ref) == B_OK;
452 i++) {
453 Playlist subPlaylist;
454 BString type = _MIMEString(&ref);
455
456 if (_IsPlaylist(type)) {
457 AppendPlaylistToPlaylist(ref, &subPlaylist);
458 // Do not sort the whole playlist anymore, as that
459 // will screw up the ordering in the saved playlist.
460 hasSavedPlaylist = true;
461 } else {
462 if (_IsQuery(type))
463 AppendQueryToPlaylist(ref, &subPlaylist);
464 else {
465 PlaylistFileReader* reader = PlaylistFileReader::GenerateReader(ref);
466 if (reader != NULL) {
467 reader->AppendToPlaylist(ref, &subPlaylist);
468 delete reader;
469 } else {
470 if (!_ExtraMediaExists(this, ref)) {
471 AppendToPlaylistRecursive(ref, &subPlaylist);
472 }
473 }
474 }
475
476 // At least sort this subsection of the playlist
477 // if the whole playlist is not sorted anymore.
478 if (sortItems && hasSavedPlaylist)
479 subPlaylist.Sort();
480 }
481
482 if (!subPlaylist.IsEmpty()) {
483 // Add to recent documents
484 be_roster->AddToRecentDocuments(&ref, kAppSig);
485 }
486
487 int32 subPlaylistCount = subPlaylist.CountItems();
488 AdoptPlaylist(subPlaylist, subAppendIndex);
489 subAppendIndex += subPlaylistCount;
490 }
491
492 if (sortItems)
493 playlist->Sort();
494
495 if (add)
496 AdoptPlaylist(temporaryPlaylist, appendIndex);
497
498 if (startPlaying) {
499 // open first file
500 SetCurrentItemIndex(0);
501 }
502 }
503
504
505 /*static*/ void
AppendToPlaylistRecursive(const entry_ref & ref,Playlist * playlist)506 Playlist::AppendToPlaylistRecursive(const entry_ref& ref, Playlist* playlist)
507 {
508 // recursively append the ref (dive into folders)
509 BEntry entry(&ref, true);
510 if (entry.InitCheck() != B_OK || !entry.Exists())
511 return;
512
513 if (entry.IsDirectory()) {
514 BDirectory dir(&entry);
515 if (dir.InitCheck() != B_OK)
516 return;
517
518 entry.Unset();
519
520 entry_ref subRef;
521 while (dir.GetNextRef(&subRef) == B_OK) {
522 AppendToPlaylistRecursive(subRef, playlist);
523 }
524 } else if (entry.IsFile()) {
525 BString mimeString = _MIMEString(&ref);
526 if (_IsMediaFile(mimeString)) {
527 PlaylistItem* item = new (std::nothrow) FilePlaylistItem(ref);
528 if (!_ExtraMediaExists(playlist, ref)) {
529 _BindExtraMedia(item);
530 if (item != NULL && !playlist->AddItem(item))
531 delete item;
532 } else
533 delete item;
534 } else
535 printf("MIME Type = %s\n", mimeString.String());
536 }
537 }
538
539
540 /*static*/ void
AppendPlaylistToPlaylist(const entry_ref & ref,Playlist * playlist)541 Playlist::AppendPlaylistToPlaylist(const entry_ref& ref, Playlist* playlist)
542 {
543 BEntry entry(&ref, true);
544 if (entry.InitCheck() != B_OK || !entry.Exists())
545 return;
546
547 BString mimeString = _MIMEString(&ref);
548 if (_IsTextPlaylist(mimeString)) {
549 //printf("RunPlaylist thing\n");
550 BFile file(&ref, B_READ_ONLY);
551 FileReadWrite lineReader(&file);
552
553 BString str;
554 entry_ref refPath;
555 status_t err;
556 BPath path;
557 while (lineReader.Next(str)) {
558 str = str.RemoveFirst("file://");
559 str = str.RemoveLast("..");
560 path = BPath(str.String());
561 printf("Line %s\n", path.Path());
562 if (path.Path() != NULL) {
563 if ((err = get_ref_for_path(path.Path(), &refPath)) == B_OK) {
564 PlaylistItem* item
565 = new (std::nothrow) FilePlaylistItem(refPath);
566 if (item == NULL || !playlist->AddItem(item))
567 delete item;
568 } else {
569 printf("Error - %s: [%" B_PRIx32 "]\n", strerror(err),
570 err);
571 }
572 } else
573 printf("Error - No File Found in playlist\n");
574 }
575 } else if (_IsBinaryPlaylist(mimeString)) {
576 BFile file(&ref, B_READ_ONLY);
577 Playlist temp;
578 if (temp.Unflatten(&file) == B_OK)
579 playlist->AdoptPlaylist(temp, playlist->CountItems());
580 }
581 }
582
583
584 /*static*/ void
AppendQueryToPlaylist(const entry_ref & ref,Playlist * playlist)585 Playlist::AppendQueryToPlaylist(const entry_ref& ref, Playlist* playlist)
586 {
587 BQueryFile query(&ref);
588 if (query.InitCheck() != B_OK)
589 return;
590
591 entry_ref foundRef;
592 while (query.GetNextRef(&foundRef) == B_OK) {
593 PlaylistItem* item = new (std::nothrow) FilePlaylistItem(foundRef);
594 if (item == NULL || !playlist->AddItem(item))
595 delete item;
596 }
597 }
598
599
600 void
NotifyImportFailed()601 Playlist::NotifyImportFailed()
602 {
603 BAutolock _(this);
604 _NotifyImportFailed();
605 }
606
607
608 /*static*/ bool
ExtraMediaExists(Playlist * playlist,PlaylistItem * item)609 Playlist::ExtraMediaExists(Playlist* playlist, PlaylistItem* item)
610 {
611 FilePlaylistItem* fileItem = dynamic_cast<FilePlaylistItem*>(item);
612 if (fileItem != NULL)
613 return _ExtraMediaExists(playlist, fileItem->Ref());
614
615 // If we are here let's see if it is an url
616 UrlPlaylistItem* urlItem = dynamic_cast<UrlPlaylistItem*>(item);
617 if (urlItem == NULL)
618 return true;
619
620 return _ExtraMediaExists(playlist, urlItem->Url());
621 }
622
623
624 // #pragma mark - private
625
626
627 /*static*/ bool
_ExtraMediaExists(Playlist * playlist,const entry_ref & ref)628 Playlist::_ExtraMediaExists(Playlist* playlist, const entry_ref& ref)
629 {
630 BString exceptExtension = _GetExceptExtension(BPath(&ref).Path());
631
632 for (int32 i = 0; i < playlist->CountItems(); i++) {
633 FilePlaylistItem* compare = dynamic_cast<FilePlaylistItem*>(playlist->ItemAt(i));
634 if (compare == NULL)
635 continue;
636 if (compare->Ref() != ref
637 && _GetExceptExtension(BPath(&compare->Ref()).Path()) == exceptExtension )
638 return true;
639 }
640 return false;
641 }
642
643
644 /*static*/ bool
_ExtraMediaExists(Playlist * playlist,BUrl url)645 Playlist::_ExtraMediaExists(Playlist* playlist, BUrl url)
646 {
647 for (int32 i = 0; i < playlist->CountItems(); i++) {
648 UrlPlaylistItem* compare
649 = dynamic_cast<UrlPlaylistItem*>(playlist->ItemAt(i));
650 if (compare == NULL)
651 continue;
652 if (compare->Url() == url)
653 return true;
654 }
655 return false;
656 }
657
658
659 /*static*/ bool
_IsImageFile(const BString & mimeString)660 Playlist::_IsImageFile(const BString& mimeString)
661 {
662 BMimeType superType;
663 BMimeType fileType(mimeString.String());
664
665 if (fileType.GetSupertype(&superType) != B_OK)
666 return false;
667
668 if (superType == "image")
669 return true;
670
671 return false;
672 }
673
674
675 /*static*/ bool
_IsMediaFile(const BString & mimeString)676 Playlist::_IsMediaFile(const BString& mimeString)
677 {
678 BMimeType superType;
679 BMimeType fileType(mimeString.String());
680
681 if (fileType.GetSupertype(&superType) != B_OK)
682 return false;
683
684 // try a shortcut first
685 if (superType == "audio" || superType == "video")
686 return true;
687
688 // Look through our supported types
689 app_info appInfo;
690 if (be_app->GetAppInfo(&appInfo) != B_OK)
691 return false;
692 BFile appFile(&appInfo.ref, B_READ_ONLY);
693 if (appFile.InitCheck() != B_OK)
694 return false;
695 BMessage types;
696 BAppFileInfo appFileInfo(&appFile);
697 if (appFileInfo.GetSupportedTypes(&types) != B_OK)
698 return false;
699
700 const char* type;
701 for (int32 i = 0; types.FindString("types", i, &type) == B_OK; i++) {
702 if (strcasecmp(mimeString.String(), type) == 0)
703 return true;
704 }
705
706 return false;
707 }
708
709
710 /*static*/ bool
_IsTextPlaylist(const BString & mimeString)711 Playlist::_IsTextPlaylist(const BString& mimeString)
712 {
713 return mimeString.Compare(kTextPlaylistMimeString) == 0;
714 }
715
716
717 /*static*/ bool
_IsBinaryPlaylist(const BString & mimeString)718 Playlist::_IsBinaryPlaylist(const BString& mimeString)
719 {
720 return mimeString.Compare(kBinaryPlaylistMimeString) == 0;
721 }
722
723
724 /*static*/ bool
_IsPlaylist(const BString & mimeString)725 Playlist::_IsPlaylist(const BString& mimeString)
726 {
727 return _IsTextPlaylist(mimeString) || _IsBinaryPlaylist(mimeString);
728 }
729
730
731 /*static*/ bool
_IsQuery(const BString & mimeString)732 Playlist::_IsQuery(const BString& mimeString)
733 {
734 return mimeString.Compare(BQueryFile::MimeType()) == 0;
735 }
736
737
738 /*static*/ BString
_MIMEString(const entry_ref * ref)739 Playlist::_MIMEString(const entry_ref* ref)
740 {
741 BFile file(ref, B_READ_ONLY);
742 BNodeInfo nodeInfo(&file);
743 char mimeString[B_MIME_TYPE_LENGTH];
744 if (nodeInfo.GetType(mimeString) != B_OK) {
745 BMimeType type;
746 if (BMimeType::GuessMimeType(ref, &type) != B_OK)
747 return BString();
748
749 strlcpy(mimeString, type.Type(), B_MIME_TYPE_LENGTH);
750 nodeInfo.SetType(type.Type());
751 }
752 return BString(mimeString);
753 }
754
755
756 // _BindExtraMedia() searches additional videos and audios
757 // and addes them as extra medias.
758 /*static*/ void
_BindExtraMedia(PlaylistItem * item)759 Playlist::_BindExtraMedia(PlaylistItem* item)
760 {
761 FilePlaylistItem* fileItem = dynamic_cast<FilePlaylistItem*>(item);
762 if (!fileItem)
763 return;
764
765 // If the media file is foo.mp3, _BindExtraMedia() searches foo.avi.
766 BPath mediaFilePath(&fileItem->Ref());
767 BString mediaFilePathString = mediaFilePath.Path();
768 BPath dirPath;
769 mediaFilePath.GetParent(&dirPath);
770 BDirectory dir(dirPath.Path());
771 if (dir.InitCheck() != B_OK)
772 return;
773
774 BEntry entry;
775 BString entryPathString;
776 while (dir.GetNextEntry(&entry, true) == B_OK) {
777 if (!entry.IsFile())
778 continue;
779 entryPathString = BPath(&entry).Path();
780 if (entryPathString != mediaFilePathString
781 && _GetExceptExtension(entryPathString) == _GetExceptExtension(mediaFilePathString)) {
782 _BindExtraMedia(fileItem, entry);
783 }
784 }
785 }
786
787
788 /*static*/ void
_BindExtraMedia(FilePlaylistItem * fileItem,const BEntry & entry)789 Playlist::_BindExtraMedia(FilePlaylistItem* fileItem, const BEntry& entry)
790 {
791 entry_ref ref;
792 entry.GetRef(&ref);
793 BString mimeString = _MIMEString(&ref);
794 if (_IsMediaFile(mimeString)) {
795 fileItem->AddRef(ref);
796 } else if (_IsImageFile(mimeString)) {
797 fileItem->AddImageRef(ref);
798 }
799 }
800
801
802 /*static*/ BString
_GetExceptExtension(const BString & path)803 Playlist::_GetExceptExtension(const BString& path)
804 {
805 int32 periodPos = path.FindLast('.');
806 if (periodPos <= path.FindLast('/'))
807 return path;
808 return BString(path.String(), periodPos);
809 }
810
811
812 // #pragma mark - notifications
813
814
815 void
_NotifyItemAdded(PlaylistItem * item,int32 index) const816 Playlist::_NotifyItemAdded(PlaylistItem* item, int32 index) const
817 {
818 BList listeners(fListeners);
819 int32 count = listeners.CountItems();
820 for (int32 i = 0; i < count; i++) {
821 Listener* listener = (Listener*)listeners.ItemAtFast(i);
822 listener->ItemAdded(item, index);
823 }
824 }
825
826
827 void
_NotifyItemRemoved(int32 index) const828 Playlist::_NotifyItemRemoved(int32 index) const
829 {
830 BList listeners(fListeners);
831 int32 count = listeners.CountItems();
832 for (int32 i = 0; i < count; i++) {
833 Listener* listener = (Listener*)listeners.ItemAtFast(i);
834 listener->ItemRemoved(index);
835 }
836 }
837
838
839 void
_NotifyItemsSorted() const840 Playlist::_NotifyItemsSorted() const
841 {
842 BList listeners(fListeners);
843 int32 count = listeners.CountItems();
844 for (int32 i = 0; i < count; i++) {
845 Listener* listener = (Listener*)listeners.ItemAtFast(i);
846 listener->ItemsSorted();
847 }
848 }
849
850
851 void
_NotifyCurrentItemChanged(int32 newIndex,bool play) const852 Playlist::_NotifyCurrentItemChanged(int32 newIndex, bool play) const
853 {
854 BList listeners(fListeners);
855 int32 count = listeners.CountItems();
856 for (int32 i = 0; i < count; i++) {
857 Listener* listener = (Listener*)listeners.ItemAtFast(i);
858 listener->CurrentItemChanged(newIndex, play);
859 }
860 }
861
862
863 void
_NotifyImportFailed() const864 Playlist::_NotifyImportFailed() const
865 {
866 BList listeners(fListeners);
867 int32 count = listeners.CountItems();
868 for (int32 i = 0; i < count; i++) {
869 Listener* listener = (Listener*)listeners.ItemAtFast(i);
870 listener->ImportFailed();
871 }
872 }
873