playlistloader.cpp
来自「Amarok是一款在LINUX或其他类UNIX操作系统中运行的音频播放器软件。 」· C++ 代码 · 共 1,109 行 · 第 1/3 页
CPP
1,109 行
// Author: Max Howell (C) Copyright 2003-4// Author: Mark Kretschmann (C) Copyright 2004// .ram file support from Kaffeine 0.5, Copyright (C) 2004 by Jürgen Kofler (GPL 2 or later)// .asx file support added by Michael Seiwert Copyright (C) 2006// .asx file support from Kaffeine, Copyright (C) 2004-2005 by Jürgen Kofler (GPL 2 or later)// .smil file support from Kaffeine 0.7// .pls parser (C) Copyright 2005 by Michael Buesch <mbuesch@freenet.de>// .xspf file support added by Mattias Fliesberg <mattias.fliesberg@gmail.com> Copyright (C) 2006// Copyright: See COPYING file that comes with this distribution/////For pls and m3u specifications see:///http://forums.winamp.com/showthread.php?s=dbec47f3a05d10a3a77959f17926d39c&threadid=65772#define DEBUG_PREFIX "PlaylistLoader"#include "amarok.h"#include "collectiondb.h"#include "debug.h"#include "enginecontroller.h"#include "mountpointmanager.h"#include "mydirlister.h"#include "playlist.h"#include "playlistbrowser.h"#include "playlistitem.h"#include "playlistloader.h"#include "statusbar.h"#include "contextbrowser.h"#include "xspfplaylist.h"#include <qdatetime.h> //::recurse()#include <qeventloop.h> //::recurse()#include <qfile.h> //::loadPlaylist()#include <qlistview.h>#include <qregexp.h>#include <qstringlist.h>#include <qtextstream.h> //::loadPlaylist()#include <dcopref.h>#include <kapplication.h>#include <kurl.h>//TODO playlists within playlists, local or remote are legal entries in m3u and pls//TODO directories from inside playlistsstruct XMLData{ MetaBundle bundle; int queue; bool stopafter; bool dynamicdisabled; XMLData(): queue(-1), stopafter(false), dynamicdisabled(false) { }};class TagsEvent : public QCustomEvent {public: TagsEvent( const QValueList<XMLData> &x ) : QCustomEvent( 1001 ), xml( QDeepCopy<QValueList<XMLData> >( x ) ) { } TagsEvent( const BundleList &bees ) : QCustomEvent( 1000 ), bundles( QDeepCopy<BundleList>( bees ) ) { for( BundleList::Iterator it = bundles.begin(), end = bundles.end(); it != end; ++it ) { (*it).detach(); /// @see MetaBundle for explanation of audioproperties < 0 if( (*it).length() <= 0 || (*it).bitrate() <= 0 ) (*it).readTags( TagLib::AudioProperties::Fast, 0 ); } } QValueList<XMLData> xml; BundleList bundles;};UrlLoader::UrlLoader( const KURL::List &urls, QListViewItem *after, int options ) : ThreadManager::DependentJob( Playlist::instance(), "UrlLoader" ) , m_markerListViewItem( new PlaylistItem( Playlist::instance(), after ) ) , m_playFirstUrl( options & (Playlist::StartPlay | Playlist::DirectPlay) ) , m_coloring( options & Playlist::Colorize ) , m_options( options ) , m_block( "UrlLoader" ) , m_oldQueue( Playlist::instance()->m_nextTracks ) , m_xmlSource( 0 ){ connect( this, SIGNAL( queueChanged( const PLItemList &, const PLItemList & ) ), Playlist::instance(), SIGNAL( queueChanged( const PLItemList &, const PLItemList & ) ) ); Playlist::instance()->lock(); // prevent user removing items as this could be bad Amarok::OverrideCursor cursor; setDescription( i18n("Populating playlist") ); Amarok::StatusBar::instance()->newProgressOperation( this ) .setDescription( m_description ) .setStatus( i18n("Preparing") ) .setAbortSlot( this, SLOT(abort()) ) .setTotalSteps( 100 ); foreachType( KURL::List, urls ) { const KURL url = Amarok::detachedKURL(Amarok::mostLocalURL( *it )); const QString protocol = url.protocol(); if( protocol == "seek" ) continue; else if( ContextBrowser::hasContextProtocol( url ) ) { DEBUG_BLOCK debug() << "context expandurl" << endl; m_URLs += ContextBrowser::expandURL(Amarok::detachedKURL(url)); } else if( !MetaBundle::isKioUrl( url ) ) { m_URLs += url; } else if( protocol == "file" ) { if( QFileInfo( url.path() ).isDir() ) m_URLs += recurse( url ); else m_URLs += url; } // Note: remove for kde 4 - we don't need to be hacking around KFileDialog, // it has been fixed for kde 3.5.3 else if( protocol == "media" || url.url().startsWith( "system:/media/" ) ) { QString path = url.path( -1 ); if( url.url().startsWith( "system:/media/" ) ) path = path.mid( 6 ); // url looks like media:/device/path DCOPRef mediamanager( "kded", "mediamanager" ); QString device = path.mid( 1 ); // remove first slash const int slash = device.find( '/' ); const QString filePath = device.mid( slash ); // extract relative path device = device.left( slash ); // extract device DCOPReply reply = mediamanager.call( "properties(QString)", device ); if( reply.isValid() ) { const QStringList properties = reply; // properties[6] is the mount point KURL localUrl = KURL( properties[6] + filePath ); // add urls if( QFileInfo( localUrl.path() ).isDir() ) m_URLs += recurse( localUrl ); else m_URLs += localUrl; } } else if( PlaylistFile::isPlaylistFile( url ) ) { debug() << "remote playlist" << endl; new RemotePlaylistFetcher( url, after, m_options ); m_playFirstUrl = false; } else { // this is the best way I found for recursing if required // and not recusring if not required const KURL::List urls = recurse( url ); // recurse only works on directories, else it swallows the URL if( urls.isEmpty() ) m_URLs += url; else m_URLs += urls; } }}UrlLoader::~UrlLoader(){ if( Playlist::instance() ) { Playlist::instance()->unlock(); if( m_markerListViewItem ) delete m_markerListViewItem; } delete m_xmlSource;}boolUrlLoader::doJob(){ setProgressTotalSteps( m_URLs.count() ); KURL::List urls; for( for_iterators( KURL::List, m_URLs ); it != end && !isAborted(); ++it ) { const KURL &url = *it; incrementProgress(); switch( PlaylistFile::format( url.fileName() ) ) { case PlaylistFile::XML: loadXml( url ); break; default: { PlaylistFile playlist( url.path() ); if( !playlist.isError() ) QApplication::postEvent( this, new TagsEvent( playlist.bundles()) ); else m_badURLs += url; } break; case PlaylistFile::NotPlaylist: (EngineController::canDecode( url ) ? urls : m_badURLs) += url; } if( urls.count() == OPTIMUM_BUNDLE_COUNT || it == last ) { QApplication::postEvent( this, new TagsEvent( CollectionDB::instance()->bundlesByUrls( urls ) ) ); urls.clear(); } } return true;}voidUrlLoader::customEvent( QCustomEvent *e){ //DEBUG_BLOCK #define e static_cast<TagsEvent*>(e) switch( e->type() ) { case 1000: foreachType( BundleList, e->bundles ) { int alreadyOnPlaylist = 0; PlaylistItem *item = 0; if( m_options & (Playlist::Unique | Playlist::Queue) ) { item = Playlist::instance()->m_urlIndex.getFirst( (*it).url() ); } if( item ) alreadyOnPlaylist++; else item = new PlaylistItem( *it, m_markerListViewItem, (*it).exists() ); if( m_options & Playlist::Queue ) Playlist::instance()->queue( item ); if( m_playFirstUrl && (*it).exists() ) { Playlist::instance()->activate( item ); m_playFirstUrl = false; } } break; case 1001: { foreachType( QValueList<XMLData>, e->xml ) { if( (*it).bundle.isEmpty() ) //safety continue; PlaylistItem* const item = new PlaylistItem( (*it).bundle, m_markerListViewItem ); item->setIsNew( m_coloring ); //TODO scrollbar position //TODO previous tracks queue //TODO current track position, even if user doesn't have resume playback turned on if( (*it).queue >= 0 ) { if( (*it).queue == 0 ) Playlist::instance()->setCurrentTrack( item ); else if( (*it).queue > 0 ) { PLItemList &m_nextTracks = Playlist::instance()->m_nextTracks; int count = m_nextTracks.count(); for( int c = count; c < (*it).queue; c++ ) // Append foo values and replace with correct values later. m_nextTracks.append( item ); m_nextTracks.replace( (*it).queue - 1, item ); } } if( (*it).stopafter ) Playlist::instance()->m_stopAfterTrack = item; item->setFilestatusEnabled( (*it).bundle.exists() ); item->setDynamicEnabled( !( (*it).dynamicdisabled ) ); } break; } default: DependentJob::customEvent( e ); return; } #undef e}voidUrlLoader::completeJob(){ DEBUG_BLOCK const PLItemList &newQueue = Playlist::instance()->m_nextTracks; QPtrListIterator<PlaylistItem> it( newQueue ); PLItemList added; for( it.toFirst(); *it; ++it ) if( !m_oldQueue.containsRef( *it ) ) added << (*it); if( !added.isEmpty() ) emit queueChanged( added, PLItemList() ); if ( !m_badURLs.isEmpty() ) { QString text = i18n("These media could not be loaded into the playlist: " ); debug() << "The following urls were not suitable for the playlist:" << endl; for ( uint it = 0; it < m_badURLs.count(); it++ ) { if( it < 5 ) text += QString("<br>%1").arg( m_badURLs[it].prettyURL() ); else if( it == 5 ) text += QString("<br>Plus %1 more").arg( m_badURLs.count() - it ); debug() << "\t" << m_badURLs[it] << endl; } Amarok::StatusBar::instance()->shortLongMessage( i18n("Some media could not be loaded (not playable)."), text ); } if( !m_dynamicMode.isEmpty() ) Playlist::instance()->setDynamicMode( PlaylistBrowser::instance()->findDynamicModeByTitle( m_dynamicMode ) ); //synchronous, ie not using eventLoop QApplication::sendEvent( dependent(), this );}KURL::ListUrlLoader::recurse( const KURL &url ){ typedef QMap<QString, KURL> FileMap; KDirLister lister( false ); lister.setAutoUpdate( false ); lister.setAutoErrorHandlingEnabled( false, 0 ); if ( !lister.openURL( url ) ) return KURL::List(); // Fucking KDirLister sometimes hangs on remote media, so we add a timeout const int timeout = 3000; // ms QTime watchdog; watchdog.start(); while( !lister.isFinished() && !isAborted() && watchdog.elapsed() < timeout ) kapp->eventLoop()->processEvents( QEventLoop::ExcludeUserInput ); KFileItemList items = lister.items(); //returns QPtrList, so we MUST only do it once! KURL::List urls; FileMap files; for( KFileItem *item = items.first(); item; item = items.next() ) { if( item->isFile() ) { files[item->name()] = item->url(); continue; } if( item->isDir() ) urls += recurse( item->url() ); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?