예제 #1
0
/** Add a track and set it as current item */
void Playlist::addCurrentTrack( Track* track )
{
    //Add if it comes from outsite only
    bool fromOutsite=true;
    PlaylistItem* currItem = (PlaylistItem*)currentItem();
    if ( currItem )
       if ( track->url() == currItem->track()->url() )
           fromOutsite=false;

    if ( fromOutsite )
        addTrack(track,NULL);
    else
        newPlaylistItem = currItem;


    if (!m_isPlaying){
        //is not playing, direct into player and roll others
        setCurrentPlaylistItem( newPlaylistItem );
    } else {
        //is playing, wait and add as next
        setNextPlaylistItem( newPlaylistItem );
    }

    handleChanges();
}
예제 #2
0
void
SourceTreeView::addToLocal()
{
    QModelIndex idx = m_contextMenuIndex;
    if ( !idx.isValid() )
        return;

    SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt();
    if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station )
    {
        DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( m_contextMenuIndex );
        dynplaylist_ptr playlist = item->dynPlaylist();

        // copy to a link and then generate a new playlist from that
        // this way we cheaply regenerate the needed controls
        QString link = GlobalActionManager::instance()->copyPlaylistToClipboard( playlist );
        GlobalActionManager::instance()->parseTomahawkLink( link );
    }
    else if ( type == SourcesModel::StaticPlaylist )
    {
        PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex );
        playlist_ptr playlist = item->playlist();

        // just create the new playlist with the same values
        QList< query_ptr > queries;
        foreach( const plentry_ptr& e, playlist->entries() )
            queries << e->query();

        playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), playlist->title(), playlist->info(), playlist->creator(), playlist->shared(), queries );
    }
예제 #3
0
bool PlaylistSaver::saveM3U(const KURL &file, int opt)
{
//	kdDebug(66666) << k_funcinfo << "file='" << file.path() << "', opt=" << opt << endl;

    bool isExt=(opt==EXTM3U); // easier ;)

    QString local(napp->tempSaveName(file.path()));
    QFile saver(local);
    saver.open(IO_ReadWrite | IO_Truncate);
    QTextStream t(&saver);

    reset();
    PlaylistItem i;
    QStringList props;

    // this is more code but otoh faster than checking for isExt inside the loop
    if(isExt)
    {
        t << "#EXTM3U" << '\n';

        while ((i=writeItem()))
        {
            int length = static_cast<int>(((i.property("length")).toInt())/1000);
            if(length==0) length=-1; // special value in an EXTM3U file, means "unknown"
            KURL u(i.property("url"));
            QString title;

            // if a playlistitem is without a tag or ONLY title is set
            if((i.property("author").isEmpty() && i.property("title").isEmpty())
                    || (i.property("author").isEmpty() && !i.property("title").isEmpty()) )
                title = u.filename().left(u.filename().length()-4);
            else
                title = i.property("author") + " - " + i.property("title");

//			kdDebug(66666) << "#EXTINF:"<< QString::number(length) << "," << title << endl;
            t << "#EXTINF:"<< QString::number(length) << "," << title << '\n';

            if (u.isLocalFile())
                t << u.path() << '\n';
            else
                t << u.url() << '\n';
        }
    }
    else
    {
        while ((i=writeItem()))
        {
            KURL u(i.property("url"));
            if (u.isLocalFile())
                t << u.path() << '\n';
            else
                t << u.url() << '\n';
        }
    }

    saver.close();
    KIO::NetAccess::upload(local, file, 0L);
    saver.remove();
    return true;
}
예제 #4
0
CopyPLItemsCommand::CopyPLItemsCommand(Playlist* playlist,
		 const int32* indices, int32 count, int32 toIndex)
	:
	PLItemsCommand(),
	fPlaylist(playlist),
	fItems(count > 0 ? new (nothrow) PlaylistItem*[count] : NULL),
	fToIndex(toIndex),
	fCount(count),
	fItemsCopied(false)
{
	if (!indices || !fPlaylist || !fItems) {
		// indicate a bad object state
		delete[] fItems;
		fItems = NULL;
		return;
	}

	memset(fItems, 0, sizeof(PlaylistItem*) * fCount);

	// init original entries and
	for (int32 i = 0; i < fCount; i++) {
		PlaylistItem* item = fPlaylist->ItemAt(indices[i]);
		if (item != NULL)
			fItems[i] = item->Clone();
		if (fItems[i] == NULL) {
			// indicate a bad object state
			_CleanUp(fItems, fCount, true);
			return;
		}
	}
}
예제 #5
0
bool PlaylistsModel::load()
{
  setUpdateSignaled(false);
  
  if (!m_provider)
    return false;
  clear();
  const SONOS::PlayerPtr player = m_provider->getPlayer();
  if (!player)
    return false;

  QString port;
  port.setNum(player->GetPort());
  QString url = "http://";
  url.append(player->GetHost().c_str()).append(":").append(port);

  SONOS::ContentDirectory cd(player->GetHost(), player->GetPort());
  SONOS::ContentList cl(cd, m_root.isEmpty() ? SONOS::ContentSearch(SONOS::SearchSonosPlaylist,"").Root() : m_root.toUtf8().constData());
  for (SONOS::ContentList::iterator it = cl.begin(); it != cl.end(); ++it)
  {
    PlaylistItem* item = new PlaylistItem(*it, url);
    if (item->isValid())
      addItem(item);
    else
      delete item;
  }
  if (cl.failure())
    return m_loaded = false;
  m_updateID = cl.GetUpdateID(); // sync new baseline
  return m_loaded = true;
}
예제 #6
0
void
SourceTreeView::copyPlaylistLink()
{
    QModelIndex idx = m_contextMenuIndex;
    if ( !idx.isValid() )
        return;

    SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( m_contextMenuIndex, SourcesModel::SourceTreeItemTypeRole ).toInt();
    if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station )
    {
        DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( m_contextMenuIndex );
        dynplaylist_ptr playlist = item->dynPlaylist();
        GlobalActionManager::instance()->copyPlaylistToClipboard( playlist );
    }
    else if ( type == SourcesModel::StaticPlaylist )
    {
        PlaylistItem* item = itemFromIndex< PlaylistItem >( m_contextMenuIndex );
        playlist_ptr playlist = item->playlist();

        QString suggestedFilename = TomahawkSettings::instance()->playlistDefaultPath() + "/" + playlist->title();
        QString filename = QFileDialog::getSaveFileName( TomahawkUtils::tomahawkWindow(), tr( "Save XSPF" ),
                                                         suggestedFilename, tr( "Playlists (*.xspf)" ) );
        if ( !filename.isEmpty() )
        {
            QFileInfo playlistAbsoluteFilePath = filename;
            TomahawkSettings::instance()->setPlaylistDefaultPath( playlistAbsoluteFilePath.absolutePath() );
            GlobalActionManager::instance()->savePlaylistToFile( playlist, filename );
        }
    }
}
예제 #7
0
void PLSimport::readFile()
{ 
 
  QFile file(filename);
  if (!file.open(QFile::ReadOnly)) {
   
    return;
  }

  QTextStream is(&file);
  
  PlaylistItem* p;
 // QString* duration;
 // QString* title;
  QString* uri; 
  QRegExp rxlen("=");
  QRegExp rxlen2("(\\d+)");
  int pos=-1;
  
  is.readLine();

  int i = rxlen2.indexIn(is.readLine());
  if(i>-1)
    i = rxlen2.cap(1).toInt();

  is.readLine();

for(int j = 0 ;j<i;j++)
{
  p= new PlaylistItem;

    uri = new QString;

    *uri  = is.readLine();

    pos = rxlen.indexIn(*uri);
    
    if (pos > -1) 
    {
      uri->remove(0, pos+1);

    }
   
    
    is.readLine();

    
    
    is.readLine();

    
    is.readLine();

    p->setURI(*uri);
    result->append(p);
  }
}
예제 #8
0
void
Playlist::MakeEmpty(bool deleteItems)
{
	int32 count = CountItems();
	for (int32 i = count - 1; i >= 0; i--) {
		PlaylistItem* item = RemoveItem(i, false);
		_NotifyItemRemoved(i);
		if (deleteItems)
			item->ReleaseReference();
	}
	SetCurrentItemIndex(-1);
}
예제 #9
0
void Playlist::addTrack( Track* track, PlaylistItem* after )
{
    if (!track || !track->isValid())
        return;
    //qDebug() << __PRETTY_FUNCTION__ <<":"<<objectName()<<" url="<<track->url();
    PlaylistItem* item =  new PlaylistItem( this, after );
    item->setTexts( track );
    newPlaylistItem  = item;
    RatingWidget* rating= new RatingWidget();
    rating->setRating( track->rate() * 0.1 );
    QObject::connect(rating,SIGNAL(RatingChanged(float)),SLOT(onRatingChanged(float)));
    setItemWidget(item, PlaylistItem::Column_Rate, rating);
}
예제 #10
0
/** Updates the playlist listbox.
*
*/
void PlaylistFrame::UpdatePlaylist()
{
	//Clear playlist
	m_lstBoxPlaylist->Clear();

	m_lstBoxPlaylist->SetForegroundColour(wxColour(0,255,0));
	PlaylistItem item;
	std::list<PlaylistItem>::const_iterator it;
	for(it = m_playlist.Begin(); it != m_playlist.End(); it++)
	{
		item = *it;
		m_lstBoxPlaylist->Append(item.GetFilename());
	}
}
예제 #11
0
/** Begin play a movie in the playlist.
* Private method.
*/
void PlaylistFrame::BeginPlayFile()
{
	if (m_itemPlaying < m_playlist.GetSize())
	{
		const Channel *channel = m_appInterface->GetCurrentChannel(); 

		//Get channel bitrate
		Mode mode;
		ModeList modeList = channel->GetModeList();
		modeList.FindMode(IRM_MODE_CHANNEL_BITRATE, mode);
		long bitrate;
		mode.GetParameter().ToLong(&bitrate);
		
		PlaylistItem item;
		std::list<PlaylistItem>::const_iterator it;
		it = m_playlist.Begin();
		for(int i = 0;i<m_itemPlaying;i++)
			it++;

		item = *it;

		bool ret = m_appInterface->MovieBeginTransmission(channel->GetName(), item.GetPath(), bitrate);

		SetFieldsStates(ret);
		m_lstBoxPlaylist->SetSelection(m_itemPlaying);
		SetStatusText(wxString::Format(_("Playing file: %s"), item.GetFilename().c_str()));

		if (!ret)
		{
			wxString msg = wxString::Format(_("Error while attempt play %s"), item.GetFilename().c_str());
			wxMessageBox(msg, wxMessageBoxCaptionStr, wxOK|wxICON_ERROR, this);
		}
	}
	else
	{
		if (m_chkBoxRepeat->IsChecked())
		{
			m_itemPlaying = 0;
			BeginPlayFile();
		}
		else
		{
			wxString msg = _("Finished send transmission of Playlist");
			wxMessageBox(msg, _("Information"), wxICON_INFORMATION|wxOK, this);
			m_appInterface->MovieCancelTransmission(m_mediaId);
			SetFieldsStates(false);
			SetStatusText(wxEmptyString);
		}
	}
}
예제 #12
0
bool PlaylistSaver::saveXML(const KURL &file, int )
{
    QString localFile;
    if (file.isLocalFile())
        localFile = QFile::encodeName(file.path());
    else
        localFile = napp->tempSaveName(file.path());

    // QDom is a pain :)
    QDomDocument doc("playlist");
    doc.setContent(QString("<!DOCTYPE XMLPlaylist><playlist version=\"1.0\" client=\"noatun\"/>"));

    QDomElement docElem=doc.documentElement();

    reset();
    PlaylistItem i;
    QStringList props;
    while ((i=writeItem()))
    {
        // write all properties
        props=i.properties();
        QDomElement elem=doc.createElement("item");
        for (QStringList::Iterator pi(props.begin()); pi!=props.end(); ++pi)
        {
            QString val=i.property(*pi);
            elem.setAttribute(*pi, val);

            if ((*pi)=="url")
            {
                KURL u(val);
                if (u.isLocalFile())
                {
                    elem.setAttribute("local", u.path());
                }
            }
        }

        docElem.appendChild(elem);
        props.clear();
    }
    Noatun::KSaver saver(localFile);
    if (!saver.open())
        return false;
    saver.textStream().setEncoding(QTextStream::UnicodeUTF8);
    saver.textStream() << doc.toString();
    saver.close();

    return true;
}
예제 #13
0
void
QueueManager::insertItems()
{
    QPtrList<PlaylistItem> list = Playlist::instance()->m_nextTracks;
    QListViewItem *last = 0;

    for( PlaylistItem *item = list.first(); item; item = list.next() )
    {
        QString title = i18n("%1 - %2").arg( item->artist(), item->title() );

        last = new QueueItem( m_listview, last, title );
        m_map[ last ] = item;
    }

    updateButtons();
}
예제 #14
0
void Playlist::addItems(const PlaylistItem &item)
{
    QVariantMap params;
    params.insert("item", item.toMap());
    params.insert("playlistid", playlistId());

    KodiConnection::sendCommand("Playlist.Add", params);

    refresh();
}
예제 #15
0
QVariantMap MediaPlayer2Player::Metadata() const
{
    QVariantMap metaData;

    // The track ID is annoying since it must result in a valid DBus object
    // path, and the regex for that is, and I quote: [a-zA-Z0-9_]*, along with
    // the normal / delimiters for paths.
    PlaylistItem *item = Playlist::playingItem();
    if (!item)
        return metaData;

    FileHandle playingFile = item->file();
    QByteArray playingTrackFileId = idFromPlaylistItem(item);

    metaData["mpris:trackid"] =
        QVariant::fromValue<QDBusObjectPath>(
                QDBusObjectPath(playingTrackFileId.constData()));

    metaData["xesam:album"] = playingFile.tag()->album();
    metaData["xesam:title"] = playingFile.tag()->title();
    metaData["xesam:artist"] = QStringList(playingFile.tag()->artist());
    metaData["xesam:genre"]  = QStringList(playingFile.tag()->genre());

    metaData["mpris:length"] = qint64(playingFile.tag()->seconds() * 1000000);
    metaData["xesam:url"] = QString::fromLatin1(
            KUrl::fromLocalFile(playingFile.absFilePath()).toEncoded());

    if(playingFile.coverInfo()->hasCover()) {
        QString fallbackFileName = KStandardDirs::locateLocal("tmp",
                QString("juk-cover-%1.png").arg(item->trackId()));

        QString path = fallbackFileName;
        if(!QFile::exists(path)) {
            path = playingFile.coverInfo()->localPathToCover(fallbackFileName);
        }

        metaData["mpris:artUrl"] = QString::fromLatin1(QUrl::fromLocalFile(
                path).toEncoded());
    }

    return metaData;
}
예제 #16
0
static void import_tool(GtkWidget *w, GTKMusicBrowser *p)
{
    FileSelector *filesel = new FileSelector(p->GetContext(),"Import a Track or Playlist into My Music");
    if (filesel->Run()) {
        FAContext *m_context = p->GetContext();
        char *returnpath = filesel->GetReturnPath();
        char *filereturn = strdup_new(returnpath);
        if (filereturn)
        {
            char *first = strtok(filereturn, "\n");
            while (first) {
                char *ext = m_context->player->GetExtension(first);
                uint32 length = strlen(first) + 10;
                char *tempurl = new char[length];

                if (IsntError(FilePathToURL(first, tempurl, &length))) 
                {
                    if (ext && m_context->plm->IsSupportedPlaylistFormat(ext))
                        p->ImportPlaylist(tempurl);
                    else if (ext && 
                             m_context->player->IsSupportedExtension(ext)) 
                    {
                        PlaylistItem *plist = new PlaylistItem(tempurl);
                        m_context->plm->RetrieveMetaDataNow(plist);

                        m_context->catalog->WriteMetaDataToDatabase(tempurl,
                                                          plist->GetMetaData());
                        m_context->catalog->AddSong(tempurl);

                        delete plist;
                    }
                }
                delete [] tempurl;
                delete ext;

                first = strtok(NULL, "\n");
            }
            delete [] filereturn;
        }
    }
    delete filesel;
}
예제 #17
0
파일: playlist.cpp 프로젝트: KDE/audex
QByteArray Playlist::toPLS(const QString& playlistPath, const bool utf8) const
{

  QStringList playlist;
  playlist.append("[Playlist]");

  int j = 0;
  for (int i = 0; i < p_playlist.count(); ++i)
  {
    PlaylistItem pi = p_playlist[i];

    if (pi.filename().isEmpty()) continue;
    ++j;

    if (!playlistPath.isEmpty())
    {
      QDir dir(playlistPath);
      playlist.append(QString("File%1=%2").arg(i+1).arg(dir.relativeFilePath(pi.filename())));
    }
    else
    {
      playlist.append(QString("File%1=%2").arg(i+1).arg(pi.filename()));
    }

    if (!pi.artist().isEmpty())
    {
      playlist.append(QString("Title%1=%2 - %3").arg(i+1).arg(pi.artist()).arg(pi.title()));
    }
    else
    {
      playlist.append(QString("Title%1=%2").arg(i+1).arg(pi.title()));
    }

    playlist.append(QString("Length%1=%2").arg(i+1).arg(pi.length()));

  }

  playlist.append(QString("NumberOfEntries=%1").arg(j));
  playlist.append(QString("Version=2"));

  if (utf8)
    return playlist.join("\n").append("\n").toUtf8();
  else
    return playlist.join("\n").append("\n").toLatin1();

}
예제 #18
0
PlaylistItem *CollectionList::createItem(const FileHandle &file, QListViewItem *, bool)
{
    // It's probably possible to optimize the line below away, but, well, right
    // now it's more important to not load duplicate items.

    if(m_itemsDict.find(file.absFilePath()))
        return 0;

    PlaylistItem *item = new CollectionListItem(file);

    if(!item->isValid()) {
        kdError() << "CollectionList::createItem() -- A valid tag was not created for \""
                  << file.absFilePath() << "\"" << endl;
        delete item;
        return 0;
    }

    setupItem(item);

    return item;
}
예제 #19
0
/** Save playlist to file.
* Private method. Return true if file was saved or false if occurs a error.
*/
bool PlaylistFrame::SavePlaylist()
{
	//If don�t have playlist, don�t create file
	if (m_playlist.GetSize() == 0)
		return false;

	wxString wildcard = _("Playlist|*.ivl");
	wxFileDialog fileDlg(this, wxFileSelectorPromptStr, wxEmptyString, wxEmptyString, wildcard, wxFD_SAVE);
	if (fileDlg.ShowModal() == wxID_OK)
	{
		wxString filePath = fileDlg.GetPath();
		if (wxFile::Access(filePath, wxFile::write))
		{
			MessageDialog msgDlg(this, _("Confirm"), _("Overwite file?"));
			if ( msgDlg.ShowModal() == ID_MESSAGEDIALOG_BTN_NO)	
				return false;
		}

#ifdef __WXGTK__
		filePath += wxT(".ivl");
		ofstream playlistFile(WX_TO_FILE_CSTR(filePath));
#else
		ofstream playlistFile(WX_TO_FILE_CSTR(filePath));
#endif
		
		PlaylistItem item;
		std::list<PlaylistItem>::iterator it;
		for(it = m_playlist.Begin(); it != m_playlist.End(); it++)
		{
			item = *it;
			playlistFile << WX_TO_FILE_CSTR(item.GetPath()) << "\n";
		}
		playlistFile.close();

		wxMessageBox(_("Playlist saved successfully."), _("Information"), wxOK, this);

	}
	return true;
	
}
예제 #20
0
파일: playlist.cpp 프로젝트: KDE/audex
void Playlist::p_add_M3U(const QByteArray& playlist)
{

  QTextStream stream(playlist, QIODevice::ReadOnly);

  QString line = stream.readLine().trimmed();

  bool extended = false;
  if (line.startsWith("#EXTM3U"))
  {
    extended = true;
    line = stream.readLine().trimmed();
  }

  PlaylistItem pi;
  while (!line.isNull())
  {

    if (line.startsWith('#'))
    {
      if (extended && line.startsWith("#EXT"))
      {
        pi = p_parse_m3u_metadata_line(line);
      }
    }
    else if (!line.isEmpty())
    {
      pi.setFilename(line);
      if (!pi.filename().isEmpty())
      {
        p_playlist.append(pi);
        pi.clear();
      }
    }

    line = stream.readLine().trimmed();

  }

}
예제 #21
0
void PlaylistWindow::savePlaylist() const //SLOT
{
    Playlist *pl = Playlist::instance();

    PlaylistItem *item = pl->firstChild();
    if( item && !item->isVisible() )
        item = static_cast<PlaylistItem*>( item->itemBelow() );

    QString title = i18n( "Untitled" );

    if( item )
    {
        QString artist = item->artist();
        QString album  = item->album();

        bool useArtist = true, useAlbum = true;

        item = static_cast<PlaylistItem*>( item->itemBelow() );

        for( ; item; item = static_cast<PlaylistItem*>( item->itemBelow() ) )
        {
            if( artist != item->artist() )
                useArtist = false;
            if( album  != item->album() )
                useAlbum = false;

            if( !useArtist && !useAlbum )
                break;
        }

        if( useArtist && useAlbum )
            title = i18n("%1 - %2").arg( artist, album );
        else if( useArtist )
            title = artist;
        else if( useAlbum )
            title = album;
    }

    QString path = PlaylistDialog::getSaveFileName( title );

    if( !path.isEmpty() && Playlist::instance()->saveM3U( path ) )
        PlaylistWindow::self()->showBrowser( "PlaylistBrowser" );
}
예제 #22
0
void
SourceTreeView::deletePlaylist( const QModelIndex& idxIn )
{
    qDebug() << Q_FUNC_INFO;

    QModelIndex idx = idxIn.isValid() ? idxIn : m_contextMenuIndex;
    if ( !idx.isValid() )
        return;

    SourcesModel::RowType type = ( SourcesModel::RowType )model()->data( idx, SourcesModel::SourceTreeItemTypeRole ).toInt();
    if ( type == SourcesModel::StaticPlaylist )
    {
        PlaylistItem* item = itemFromIndex< PlaylistItem >( idx );
        playlist_ptr playlist = item->playlist();
        Playlist::remove( playlist );
    }
    else if( type == SourcesModel::AutomaticPlaylist || type == SourcesModel::Station )
    {
        DynamicPlaylistItem* item = itemFromIndex< DynamicPlaylistItem >( idx );
        dynplaylist_ptr playlist = item->dynPlaylist();
        DynamicPlaylist::remove( playlist );
    }
}
예제 #23
0
void YoutubeProtocolHandler::execute(const QUrl &uri, bool queue)
{
    QStringList parts = uri.path().split('/');
    QUrlQuery query;
    if (parts.count()) {
        query.addQueryItem("action", "play_video");
        query.addQueryItem("videoid", parts.last());
    }

    if (query.isEmpty()) {
        return;
    }

    PlaylistItem item;
    item.setFile("plugin://plugin.video.youtube/?" + query.toString());

    Player *player = Kodi::instance()->videoPlayer();
    if (queue) {
        player->playlist()->addItems(item);
    } else {
        player->open(item);
    }
}
예제 #24
0
파일: playlist.cpp 프로젝트: KDE/audex
const PlaylistItem Playlist::p_parse_m3u_metadata_line(const QString& line)
{

  PlaylistItem pi;

  QString info = line.section(':', 1);
  QString l = info.section(',', 0, 0);
  bool ok;
  int length = l.toInt(&ok);
  if (!ok) return pi;
  pi.setLength(length);

  QString track_info = info.section(',', 1);
  QStringList list = track_info.split('-');
  if (list.length() <= 1)
  {
    pi.setTitle(track_info);
    return pi;
  }
  pi.setArtist(list[0].trimmed());
  pi.setTitle(list[1].trimmed());
  return pi;

}
예제 #25
0
파일: player.cpp 프로젝트: valir/vpd
void play() {
  currentPlaylistItem = playlist_.current();
  if (currentPlaylistItem.empty()) {
    BOOST_LOG_TRIVIAL(debug)
        << "Playlist is currently empty, or we reached its end.";
    return;
  }

  for (;;) {
    if (play(currentPlaylistItem.uri_)) break;
    // no need to update status now. it'll get updated when client will ask for
    // it
    // if the item could not be played, then automatically continue with next
    // item
    BOOST_LOG_TRIVIAL(debug) << "Trying next item in the playlist";

    currentPlaylistItem = playlist_.next();
    if (currentPlaylistItem.empty()) {
      BOOST_LOG_TRIVIAL(debug)
          << "Playlist is currently empty, or we reached its end.";
      return;
    }
  }
}
예제 #26
0
파일: playlist.cpp 프로젝트: KDE/audex
QByteArray Playlist::toM3U(const QString& playlistPath, const bool utf8) const {

  QStringList playlist;
  playlist.append("#EXTM3U");

  for (int i = 0; i < p_playlist.count(); ++i)
  {

    PlaylistItem pi = p_playlist[i];
    if (pi.filename().isEmpty()) continue;

    if (!pi.artist().isEmpty())
    {
      playlist.append(QString("#EXTINF:%1,%2 - %3").arg(pi.length()).arg(pi.artist()).arg(pi.title()));
    }
    else
    {
      playlist.append(QString("#EXTINF:%1,%2").arg(pi.length()).arg(pi.title()));
    }
    if (!playlistPath.isEmpty())
    {
      QDir dir(playlistPath);
      playlist.append(dir.relativeFilePath(pi.filename()));
    }
    else
    {
      playlist.append(pi.filename());
    }

  }

  if (utf8)
    return playlist.join("\n").append("\n").toUtf8();
  else
    return playlist.join("\n").append("\n").toLatin1();

}
예제 #27
0
파일: playlist.cpp 프로젝트: KDE/audex
QByteArray Playlist::toXSPF() const
{

  QDomDocument doc;
  QDomElement root = doc.createElement("playlist");
  root.setAttribute("version", "1");
  root.setAttribute("xmlns", "http://xspf.org/ns/0");

  QDomElement creator = doc.createElement("creator");
  QDomText text = doc.createTextNode("audex");
  creator.appendChild(text);
  root.appendChild(creator);

  QDomElement tracklist = doc.createElement("trackList");

  int j = 0;
  for (int i = 0; i < p_playlist.count(); ++i)
  {

    PlaylistItem pi = p_playlist[i];

    if (pi.filename().isEmpty()) continue;
    ++j;

    QDomElement track = doc.createElement("track");

    QDomElement ch = doc.createElement("location");
    QDomText text = doc.createTextNode(pi.filename());

    ch.appendChild(text);
    track.appendChild(ch);

    if (!pi.artist().isEmpty())
    {
      ch = doc.createElement("creator");
      text = doc.createTextNode(pi.artist());
      ch.appendChild(text);
      track.appendChild(ch);
    }

    ch = doc.createElement("title");
    text = doc.createTextNode(pi.title());
    ch.appendChild(text);
    track.appendChild(ch);

    ch = doc.createElement("trackNum");
    text = doc.createTextNode(QString::number(j));
    ch.appendChild(text);
    track.appendChild(ch);

    if (pi.length() > 0)
    {
      ch = doc.createElement("duration");
      text = doc.createTextNode(QString::number(pi.length()*1000));
      ch.appendChild(text);
      track.appendChild(ch);
    }

    tracklist.appendChild(track);

  }

  root.appendChild(tracklist);
  doc.appendChild(root);
  QByteArray xml_header("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  return doc.toByteArray().prepend(xml_header);

}
예제 #28
0
void VorbisLMC::DecodeWork()
{
   void          *pOutBuffer;
   Error          Err;
   int32          iValue;
   int32          section, ret;
   OutputInfo    *info;
   vorbis_info   *vi;
   uint32         bytesCopied, bytesPerFrame;
   int            bitrateLoops = 0;

   assert(m_pPmi);
   assert(m_pPmo);

   m_pSleepSem->Wait();
   m_pPmi->Wake();

   Err = CanDecode();
   if (Err == kError_Interrupt)
      return;
   if (Err != kError_NoErr)
   {
       m_pContext->log->Error("CanDecode returned false.\n");
       if (m_decodeInfo.sendInfo)
       {
           ReportStatus(szCannotDecode);
           m_pTarget->AcceptEvent(new Event(INFO_DoneOutputtingDueToError));
       }
       else
           ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
       return;
   }

   Err = ExtractMediaInfo();
   if (Err == kError_Interrupt)
      return;

   if (IsError(Err))
   {
       m_pContext->log->Error("ExtractMediaInfo failed: %d\n", Err);
       if (m_decodeInfo.sendInfo)
       {
           ReportStatus(szCannotDecode);
           m_pTarget->AcceptEvent(new Event(INFO_DoneOutputtingDueToError));
       }
       else
           ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOErrorEvent());
       return;
   }

   if (!m_bInit)
   {
       Err = InitDecoder();
       if (Err == kError_Interrupt)
          return;

       if (IsError(Err))
       {
           m_pContext->log->Error("Initializing the decoder failed: %d\n", Err);
           ReportError("Initializing the decoder failed.");
    
           return;
       }
   }

   m_pContext->prefs->GetPrefInt32(kDecoderThreadPriorityPref, &iValue);
   m_decoderThread->SetPriority(iValue);

   bytesCopied = 0;
   bytesPerFrame = 1;
   for (m_frameCounter = 0; !m_bExit;)
   {
      if (m_bPause)
      {
          m_pPauseSem->Wait();
          if (m_bExit)
              break;
      }

      if (m_newPos >= 0)
      {   
          ov_time_seek(&m_vf, (double)(m_newPos / iFramesPerSecond));
          m_frameCounter = m_newPos - 1;
          m_newPos = -1;
          bytesCopied = bytesPerFrame;
      }

      if (bytesCopied >= bytesPerFrame)
      {
          m_frameCounter += bytesCopied / bytesPerFrame;
          bytesCopied %= bytesPerFrame;
          ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(
             new PMOTimeInfoEvent(m_frameCounter));

          bitrateLoops++;
          if (bitrateLoops == iBitrateLoopsPerUpdate && m_decodeInfo.sendInfo)
          {
             int b;

             b = ov_bitrate_instant(&m_vf),
             vi = ov_info(&m_vf, -1);
             VorbisInfoEvent *mie = new VorbisInfoEvent(b,
                                           vi->channels, 
                                           vi->rate, 
                                           1. / (float)iFramesPerSecond);
             m_pTarget->AcceptEvent(mie);

             bitrateLoops = 0;
          }
      }

      Err = m_pOutputBuffer->BeginWrite(pOutBuffer, iDecodeBlockSize);
      if (Err == kError_Interrupt)
      {
          break;
      }
      if (Err == kError_BufferTooSmall)
      {
          if (Sleep())
             break;

          continue;
      }
      if (Err != kError_NoErr)
      {
          ReportError(szFailWrite);
          m_pContext->log->Error("LMC: Cannot write to eventbuffer: %s (%d)\n",
                                  m_szError, Err);
          break;
      } 

      section = -1;
      ret = ov_read(&m_vf, (char *)pOutBuffer, iDecodeBlockSize, 
                    0, 2, 1, &section);
      if (ret == 0)
      {
         m_pOutputBuffer->EndWrite(0);
         break;
      }

      if (section != m_section)
      { 
          vi = ov_info(&m_vf, -1);

          info = new OutputInfo;
          info->bits_per_sample = 16;
          info->number_of_channels = m_channels = vi->channels;
          info->samples_per_second = m_rate = vi->rate;
          info->samples_per_frame = vi->rate / iFramesPerSecond;
          info->max_buffer_size = 16384;
          m_frameCounter = 0;
          bytesCopied = 0;
          bytesPerFrame = (vi->rate / iFramesPerSecond) * 
                          sizeof(ogg_int16_t) * vi->channels;
          m_section = section;
       
          m_pOutputBuffer->EndWrite(0);
          ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOInitEvent(info));
          ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(
             new PMOTimeInfoEvent(m_frameCounter));

          Err = m_pOutputBuffer->BeginWrite(pOutBuffer, iDecodeBlockSize);
          if (Err != kError_NoErr)
          {
             assert(0);
          }

          vorbis_comment *comment;

          comment = ov_comment(&m_vf, -1);
          if (comment)
          {
              PlaylistItem *plItem = m_pContext->plm->GetCurrentItem();
              if (plItem)
              {
                  MetaData mdata = plItem->GetMetaData();
                  string iso;

                  char *temp;
                  temp = vorbis_comment_query(comment, "title", 0);
                  if (temp)
                  {
                      iso = ConvertToISO(temp);
                      mdata.SetTitle(iso);
                  }
 
                  temp = vorbis_comment_query(comment, "artist", 0);
                  if (temp)
                  {
                      iso = ConvertToISO(temp);
                      mdata.SetArtist(iso);
                  }

                  temp = vorbis_comment_query(comment, "album", 0);
                  if (temp)
                  {
                      iso = ConvertToISO(temp);
                      mdata.SetAlbum(iso);
                  }

                  temp = vorbis_comment_query(comment, "tracknumber", 0);
                  if (temp)
                      mdata.SetTrack(atoi(temp));

                  plItem->SetMetaData(&mdata);
                  m_pContext->target->AcceptEvent(
                             new PlaylistCurrentItemInfoEvent(plItem, 
                                                              m_pContext->plm));

              }
          }
      }
      if(ret <0) 
         ret=0; // hole/error in data - we can safely ignore this
      m_pOutputBuffer->EndWrite(ret);

      bytesCopied += ret;
   }
   ((EventBuffer *)m_pOutputBuffer)->AcceptEvent(new PMOQuitEvent());
   ov_clear(&m_vf);

   return;
}
예제 #29
0
Error PMP300::WritePlaylist(DeviceInfo* device, 
                            vector<PlaylistItem*>* list,
                            PLMCallBackFunction function,
                            void* cookie)
{
    Error result = kError_InvalidParam;

    assert(device);
    assert(list);

    if(device && list)
    {
        result = kError_DeviceNotFound;

        if(!strcasecmp(device->GetDevice(), devices[0].device))
        {
            CRio rioInternal, rioExternal;
            bool rioPresent = false;

            rioPresent = FindRio(rioInternal, device, function, cookie);

            if(rioPresent)
            {
                FindRio(rioExternal, device, function, cookie);

                rioExternal.UseExternalFlash(true);

                if(function)
                {
                    PLMEvent event;
    
                    event.type = kPLMEvent_Status;
                    event.eventString += "A ";
                    event.eventString = device->GetDevice();
                    event.eventString += " has been found. Scanning internal memory...";

                    function(&event, cookie);
                }     

                vector<PlaylistItem*> origInternal;
                vector<PlaylistItem*> origExternal;
                vector<PlaylistItem*> newInternal;
                vector<PlaylistItem*> newExternal;
                uint32 externalTotal = 0, internalTotal = 0, usedMem; 
                uint32 count, index;
                char* path = new char[_MAX_PATH];
                char* tempPath = new char[_MAX_PATH];
                uint32 length = _MAX_PATH;

                // get a temp path
                m_context->prefs->GetPrefString(kInstallDirPref, tempPath, &length);
                strcat(tempPath, tmpnam(NULL));

#ifdef WIN32
                mkdir(tempPath);
#else
                mkdir(tempPath, S_IRWXU);
#endif

                // first get current state of device
                // we break our lists into internal and 
                // external lists for ease of organizing
                result = privateReadPlaylist(rioInternal, 
                                             false,  
                                             &internalTotal,
                                             &usedMem,
                                             &origInternal, 
                                             function, 
                                             cookie); 
                
                if(IsntError(result))
                {
                    if(function)
                    {
                        PLMEvent event;

                        event.type = kPLMEvent_Status;
                        event.eventString = "Scanning external memory...";

                        function(&event, cookie);
                    }

                    privateReadPlaylist(rioExternal, 
                                        true,  
                                        &externalTotal,
                                        &usedMem,
                                        &origExternal, 
                                        function, 
                                        cookie);    
                }

                count = list->size();
                bool useExternal = false;

                for(index = 0; index < count; index++)
                {
                    PlaylistItem* item = (*list)[index];
                    
                    if(item)
                    {
                        MetaData metadata = item->GetMetaData();

                        int32 size = metadata.Size();

                        if(!size)
                        {
                            struct stat st;

                            length = _MAX_PATH;

                            URLToFilePath(item->URL().c_str(), path, &length);

                            if(!stat(path, &st))
                                size = st.st_size;
                            else
                            {
                                result = kError_FileNoAccess;
                                break;
                            }
                        }
                        
                        // figure out where to put it...
                        uint32* memorySize;
                        vector<PlaylistItem*>* addList;

                        if(!useExternal)
                        {
                            memorySize = &internalTotal;
                            addList = &newInternal;

                            if(*memorySize < (uint32)size)
                                useExternal = true;
                            else
                                *memorySize -= size;
                        }
                        
                        if(useExternal)
                        {
                            memorySize = &externalTotal;
                            addList = &newExternal;

                            if(*memorySize < (uint32)size)
                                break;
                            else
                                *memorySize -= size;
                        }
                        
                        addList->push_back(item);
                    }
                }

                // if all is well we delete old files
                // and temporarily download files
                // that are being moved from internal
                // to external and vice versa...
                if(IsntError(result))
                {
                    if(function)
                    {
                        PLMEvent event;

                        event.type = kPLMEvent_Status;
                        event.eventString = "Deleting files...";

                        function(&event, cookie);
                    }

                    count = origInternal.size();

                    for(index = 0; index < count; index++)
                    {
                        PlaylistItem* item = origInternal[index];

                        if(find_if(newInternal.begin(), 
                                   newInternal.end(), 
                                   PlaylistItemCompare(item)) == newInternal.end())
                        {
                            // need to delete it
                            vector<PlaylistItem*>::iterator position;

                            if((position = find_if(newExternal.begin(), 
                                                   newExternal.end(), 
                                                   PlaylistItemCompare(item))) != newExternal.end())
                            {
                                // need to download it to temp file first
                                // and then upload it to other card
                                // in the next stage...

                                string itemPath = item->URL();
                                string downloadPath = tempPath;
                                downloadPath += DIR_MARKER_STR;

                                downloadPath.insert(downloadPath.size(), 
                                                    itemPath, 
                                                    itemPath.rfind('/') + 1, 
                                                    itemPath.size());

                                RioProgressStruct ps;

                                memset(&ps, 0x00, sizeof(ps));

                                ps.function = function;
                                ps.cookie = cookie;
                                ps.item = item;

                                if(!rioInternal.RxFile(downloadPath.c_str(), rioProgress, &ps))
                                {
                                    if(function)
                                    {
                                        PLMEvent event;
    
                                        event.type = kPLMEvent_Error;
                                        event.data.errorData.errorCode = rioInternal.GetErrorID();
                                        event.eventString = "Download failed, ";
                                        event.eventString += rioInternal.GetErrorStr();

                                        function(&event, cookie);
                                    }

                                    if(rioInternal.GetErrorID() == CRIO_ERROR_INTERRUPTED)
                                        result = kError_UserCancel;
                                    else
                                        result = kError_UnknownErr;
                                    break;
                                }

                                length = _MAX_PATH;
                                FilePathToURL(downloadPath.c_str(), path, &length);

                                (*position)->SetURL(path);
                            }

                            string::size_type pos = item->URL().rfind("/") + 1;
                            const char* cp = item->URL().c_str();

                            if(!rioInternal.RemoveFile(cp + pos))
                            {
                                if(function)
                                {
                                    PLMEvent event;
            
                                    event.type = kPLMEvent_Error;
                                    event.data.errorData.errorCode = rioInternal.GetErrorID();
                                    event.eventString = "Delete failed, ";
                                    event.eventString += rioInternal.GetErrorStr();

                                    function(&event, cookie);
                                }

                                result = kError_UnknownErr;
                                break;
                            }

                            delete item;
                            origInternal[index] = NULL;
                        }
                    }

                    if(IsntError(result))
                    {
                        count = origExternal.size();

                        for(index = 0; index < count; index++)
                        {
                            PlaylistItem* item = origExternal[index];

                            if(find_if(newExternal.begin(), 
                                       newExternal.end(), 
                                       PlaylistItemCompare(item)) == newExternal.end())
                            {
                                // need to delete it

                                vector<PlaylistItem*>::iterator position;

                                if((position = find_if(newInternal.begin(), 
                                                    newInternal.end(), 
                                                    PlaylistItemCompare(item))) != newInternal.end())
                                {
                                    // need to download it to temp file first
                                    // and then upload it to other card
                                    // in the next stage...
                                    
                                    string itemPath = item->URL();
                                    string downloadPath = tempPath;
                                    downloadPath += DIR_MARKER_STR;

                                    downloadPath.insert(downloadPath.size(), 
                                                        itemPath, 
                                                        itemPath.rfind('/') + 1, 
                                                        itemPath.size());

                                    RioProgressStruct ps;

                                    memset(&ps, 0x00, sizeof(ps));

                                    ps.function = function;
                                    ps.cookie = cookie;
                                    ps.item = item;

                                    if(!rioExternal.RxFile(downloadPath.c_str(), rioProgress, &ps))
                                    {
                                        if(function)
                                        {
                                            PLMEvent event;
        
                                            event.type = kPLMEvent_Error;
                                            event.data.errorData.errorCode = rioExternal.GetErrorID();
                                            event.eventString = "Download failed, ";
                                            event.eventString += rioExternal.GetErrorStr();

                                            function(&event, cookie);
                                        }

                                        if(rioInternal.GetErrorID() == CRIO_ERROR_INTERRUPTED)
                                            result = kError_UserCancel;
                                        else
                                            result = kError_UnknownErr;
                                        break;
                                    }

                                    length = _MAX_PATH;
                                    FilePathToURL(downloadPath.c_str(), path, &length);

                                    (*position)->SetURL(path);
                                }

                                string::size_type pos = item->URL().rfind("/") + 1;
                                const char* cp = item->URL().c_str();

                                if(!rioExternal.RemoveFile(cp + pos))
                                {
                                    if(function)
                                    {
                                        PLMEvent event;
        
                                        event.type = kPLMEvent_Error;
                                        event.data.errorData.errorCode = rioExternal.GetErrorID();
                                        event.eventString = "Delete failed, ";
                                        event.eventString += rioExternal.GetErrorStr();

                                        function(&event, cookie);
                                    }

                                    result = kError_UnknownErr;
                                    break;
                                }

                                delete item;
                                origExternal[index] = NULL;
                            }
                        }
                    }
                }

                // if all is well we add new files
                // to each card
                if(IsntError(result))
                {                 
                    // remove NULLs caused by deletes
                    origInternal.erase(
                        remove(origInternal.begin(), 
                               origInternal.end(), 
                               (PlaylistItem*)NULL), 
                        origInternal.end());

                    origExternal.erase(
                        remove(origExternal.begin(), 
                               origExternal.end(), 
                               (PlaylistItem*)NULL), 
                        origExternal.end());

                    // sync deletes back to the cards
                    rioInternal.TxDirectory();
                    rioExternal.TxDirectory();

                    if(function)
                    {
                        PLMEvent event;

                        event.type = kPLMEvent_Status;
                        event.eventString = "Uploading new files...";

                        function(&event, cookie);
                    }

                    count = newInternal.size();

                    for(index = 0; index < count; index++)
                    {
                        PlaylistItem* item = newInternal[index];

                        if(item->URL().find("file://") != string::npos)
                        {
                            length = _MAX_PATH;

                            URLToFilePath(item->URL().c_str(), path, &length);

                            RioProgressStruct ps;

                            memset(&ps, 0x00, sizeof(ps));

                            ps.function = function;
                            ps.cookie = cookie;
                            ps.item = item;
                            
                            if(!rioInternal.TxFile(path, rioProgress, &ps))
                            {
                                if(function)
                                {
                                    PLMEvent event;
        
                                    event.type = kPLMEvent_Error;
                                    event.data.errorData.errorCode = rioInternal.GetErrorID();
                                    event.eventString = "Upload failed, ";
                                    event.eventString += rioInternal.GetErrorStr();

                                    function(&event, cookie);
                                }

                                if(rioInternal.GetErrorID() == CRIO_ERROR_INTERRUPTED)
                                    result = kError_UserCancel;
                                else
                                    result = kError_UnknownErr;
                                break;
                            }

                            origInternal.push_back(new PlaylistItem(*item));
                        }
                    }

                    if(IsntError(result))
                    {
                        count = newExternal.size();

                        for(index = 0; index < count; index++)
                        {
                            PlaylistItem* item = newExternal[index];

                            if(item->URL().find("file://") != string::npos)
                            {
                                length = _MAX_PATH;

                                URLToFilePath(item->URL().c_str(), path, &length);

                                RioProgressStruct ps;

                                memset(&ps, 0x00, sizeof(ps));

                                ps.function = function;
                                ps.cookie = cookie;
                                ps.item = item;
                            
                                if(!rioExternal.TxFile(path, rioProgress, &ps))
                                {
                                    if(function)
                                    {
                                        PLMEvent event;
        
                                        event.type = kPLMEvent_Error;
                                        event.data.errorData.errorCode = rioExternal.GetErrorID();
                                        event.eventString = "Upload failed, ";
                                        event.eventString += rioExternal.GetErrorStr();

                                        function(&event, cookie);
                                    }

                                    if(rioExternal.GetErrorID() == CRIO_ERROR_INTERRUPTED)
                                        result = kError_UserCancel;
                                    else
                                        result = kError_UnknownErr;
                                    break;
                                }

                                origExternal.push_back(new PlaylistItem(*item));
                            }
                        }
                    }
                }

                // finally put it all in the correct order
                // and we should be done!
                if(IsntError(result))
                {
                    uint32 entryOrder[ CRIO_MAX_DIRENTRY ];

	                count = newInternal.size();

                    if(count)
                    {

                        for(index = 0; index < count; index++)
                        {
                            vector<PlaylistItem*>::iterator position;
                            PlaylistItem* item = newInternal[index];

                            if((position = find_if(origInternal.begin(), 
                                                  origInternal.end(), 
                                                  PlaylistItemCompare(item))) != origInternal.end())
                            {
//#ifdef WIN32
//								distance(origInternal.begin(), position /*, entryOrder[index] */ );
//#else
								entryOrder[index] = distance(origInternal.begin(), position /*, entryOrder[index] */ );
//#endif
                            }

                        }

                        rioInternal.SetFileOrder(entryOrder, count);
                    }

                    count = newExternal.size();

                    if(count)
                    {
                        for(index = 0; index < count; index++)
                        {
                            vector<PlaylistItem*>::iterator position;
                            PlaylistItem* item = newExternal[index];

                            if((position = find_if(origExternal.begin(), 
                                                  origExternal.end(), 
                                                  PlaylistItemCompare(item))) != origExternal.end())
                            {
//#ifdef WIN32
//                                distance(origExternal.begin(), position /*, entryOrder[index] */ );
//#else	                            
								entryOrder[index] = distance(origExternal.begin(), position /*, entryOrder[index] */ );
//#endif

                            }
                        }

                        rioExternal.SetFileOrder(entryOrder, count);
                    }

                    // sync uploads back to the cards
                    rioInternal.TxDirectory();
                    rioExternal.TxDirectory();
                }

                if(function)
                {
                    PLMEvent event;

                    event.type = kPLMEvent_Done;

                    function(&event, cookie);
                }

                // clean up
                length = _MAX_PATH;
                FilePathToURL(tempPath, path, &length);
                strcpy(tempPath, path);

                count = origInternal.size();

                for(index = 0; index < count; index++)
                {
                    PlaylistItem* item = origInternal[index];

                    if(item->URL().find(tempPath) != string::npos)
                    {
                        length = _MAX_PATH;

                        URLToFilePath(item->URL().c_str(), path, &length);

                        remove(path);
                    }

                    delete item;
                }

                count = origExternal.size();

                for(index = 0; index < count; index++)
                {
                    PlaylistItem* item = origExternal[index];

                    if(item->URL().find(tempPath) != string::npos)
                    {
                        length = _MAX_PATH;

                        URLToFilePath(item->URL().c_str(), path, &length);

                        remove(path);
                    }

                    delete item;
                }
 
                
                URLToFilePath(tempPath, path, &length);
                rmdir(path);

                delete [] tempPath;
                delete [] path;
            }
        }       
    }

    return result;
}
예제 #30
0
// ValidateItemLayout
void
SlideShowPlaylist::ValidateItemLayout()
{
	int64 duration = Value(PROPERTY_DURATION, (int64)0);
	if (duration == 0)
		return;

	int64 transitionDuration
		= Value(PROPERTY_TRANSITION_DURATION, (int64)0);
	// TODO: transition mode...

	int32 count = CountItems();

	BList managedItems;

	int64 minDuration = 0;
	int64 minItemDuration = transitionDuration * 3;
	int64 fixedItemsDuration = 0;
	int64 maxDuration = 0;
	int32 variableItemCount = 0;
	for (int32 i = 0; i < count; i++) {
		PlaylistItem* item = ItemAtFast(i);
		if (item->Track() > 1)
			// not a "managed" item
			continue;

		managedItems.AddItem(item);

		if (item->HasMaxDuration()) {
			int64 maxItemDuration = item->MaxDuration();
			minDuration += maxItemDuration;
			fixedItemsDuration += maxItemDuration;
			if (minItemDuration > maxItemDuration)
				minItemDuration = maxItemDuration;
		} else {
			minDuration += 3 * transitionDuration;
			variableItemCount++;
		}

		maxDuration += item->MaxDuration();
	}

	count = managedItems.CountItems();
	if (count == 0)
		return;

	if (duration < minDuration)
		duration = minDuration;
	if (duration > maxDuration)
		duration = maxDuration;

	// limit transition duration to 1/3 of the minimum item duration
	int64 maxTransitionDuration = minItemDuration / 3;

	if (transitionDuration > maxTransitionDuration)
		transitionDuration = maxTransitionDuration;

	int64 variableItemsDuration = duration - fixedItemsDuration
			+ transitionDuration * (count - variableItemCount);

	int64 startFrame = 0;
	int64 lastVariableStartFrame = 0;
	int32 variableItemIndex = 0;
	for (int32 i = 0; i < count; i++) {
		PlaylistItem* item = (PlaylistItem*)managedItems.ItemAtFast(i);
		// overlapping items
		item->SetClipOffset(0);
		item->SetTrack(i & 1);

		int64 nextStartFrame;
		if (item->HasMaxDuration()) {
			nextStartFrame = startFrame + item->MaxDuration()
								- transitionDuration;
		} else {
			variableItemIndex++;
			int64 nextVariableStartFrame = (variableItemsDuration - transitionDuration)
								* variableItemIndex / variableItemCount;
			nextStartFrame = startFrame + nextVariableStartFrame - lastVariableStartFrame;
			lastVariableStartFrame = nextVariableStartFrame;
		}
		item->SetStartFrame(startFrame);
		item->SetDuration(nextStartFrame - startFrame + transitionDuration);
		startFrame = nextStartFrame;

		// transition
		PropertyAnimator* animator = item->AlphaAnimator();
		if (!animator)
			continue;

		AutoNotificationSuspender _(animator);

		// remove all keyframes to get a clean start
		animator->MakeEmpty();
		KeyFrame* first = animator->InsertKeyFrameAt(0LL);
		KeyFrame* last = animator->InsertKeyFrameAt(item->Duration() - 1);

		if (!first || !last)
			continue;

		first->SetScale(1.0);
		last->SetScale(1.0);

		// transition in top items
		if (transitionDuration > 0 && !(i & 1)) {
			// item on first track, animated opacity property
			if (i > 0) {
				// fade in
				KeyFrame* key = animator->InsertKeyFrameAt(transitionDuration);
				key->SetScale(1.0);
				first->SetScale(0.0);
			}
			
			if (i < count - 1) {
				// fade out
				KeyFrame* key = animator->InsertKeyFrameAt(
									item->Duration() - 1 - transitionDuration);
				key->SetScale(1.0);
				last->SetScale(0.0);
			}
		}
	}
}