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 + -
显示快捷键?