File indexing completed on 2024-05-05 04:48:29

0001 /****************************************************************************************
0002  * Copyright (c) 2008 Daniel Caleb Jones <danielcjones@gmail.com>                       *
0003  * Copyright (c) 2013 Ralf Engels <ralf-engels@gmx.de>                                  *
0004  *                                                                                      *
0005  * This program is free software; you can redistribute it and/or modify it under        *
0006  * the terms of the GNU General Public License as published by the Free Software        *
0007  * Foundation; either version 2 of the License, or (at your option) version 3 or        *
0008  * any later version accepted by the membership of KDE e.V. (or its successor approved  *
0009  * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of  *
0010  * version 3 of the license.                                                            *
0011  *                                                                                      *
0012  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0013  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0014  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0015  *                                                                                      *
0016  * You should have received a copy of the GNU General Public License along with         *
0017  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0018  ****************************************************************************************/
0019 
0020 #define DEBUG_PREFIX "BiasedPlaylist"
0021 
0022 #include "BiasedPlaylist.h"
0023 
0024 #include "App.h"
0025 #include "amarokconfig.h"
0026 #include "core/collections/Collection.h"
0027 #include "core/logger/Logger.h"
0028 #include "core/meta/Meta.h"
0029 #include "core/support/Components.h"
0030 #include "core/support/Debug.h"
0031 #include "core-impl/collections/support/CollectionManager.h"
0032 #include "dynamic/BiasSolver.h"
0033 #include "dynamic/BiasFactory.h"
0034 #include "dynamic/DynamicModel.h"
0035 #include "playlist/PlaylistModelStack.h" // for The::playlist
0036 
0037 #include <QThread>
0038 #include <QXmlStreamReader>
0039 #include <QXmlStreamWriter>
0040 
0041 Dynamic::BiasedPlaylist::BiasedPlaylist( QObject *parent )
0042     : DynamicPlaylist( parent )
0043     , m_bias( nullptr )
0044     , m_solver( nullptr )
0045 {
0046     m_title = i18nc( "Title for a default dynamic playlist. The default playlist only returns random tracks.", "Random" );
0047 
0048     BiasPtr biasPtr( BiasPtr( new Dynamic::RandomBias() ) );
0049     biasReplaced( BiasPtr(), biasPtr );
0050 }
0051 
0052 Dynamic::BiasedPlaylist::BiasedPlaylist( QXmlStreamReader *reader, QObject *parent )
0053     : DynamicPlaylist( parent )
0054     , m_bias( nullptr )
0055     , m_solver( nullptr )
0056 {
0057     while (!reader->atEnd()) {
0058         reader->readNext();
0059 
0060         if( reader->isStartElement() )
0061         {
0062             QStringRef name = reader->name();
0063             if( name == "title" )
0064                 m_title = reader->readElementText(QXmlStreamReader::SkipChildElements);
0065             else
0066             {
0067                 BiasPtr biasPtr( Dynamic::BiasFactory::fromXml( reader ) );
0068                 if( biasPtr )
0069                 {
0070                     biasReplaced( BiasPtr(), biasPtr );
0071                 }
0072                 else
0073                 {
0074                     debug()<<"Unexpected xml start element"<<reader->name()<<"in input";
0075                     reader->skipCurrentElement();
0076                 }
0077             }
0078         }
0079         else if( reader->isEndElement() )
0080         {
0081             break;
0082         }
0083     }
0084 }
0085 
0086 Dynamic::BiasedPlaylist::~BiasedPlaylist()
0087 {
0088     requestAbort();
0089 }
0090 
0091 void
0092 Dynamic::BiasedPlaylist::toXml( QXmlStreamWriter *writer ) const
0093 {
0094     writer->writeTextElement( QStringLiteral("title"), m_title );
0095     writer->writeStartElement( m_bias->name() );
0096     m_bias->toXml( writer );
0097     writer->writeEndElement();
0098 }
0099 
0100 void
0101 Dynamic::BiasedPlaylist::requestAbort()
0102 {
0103     DEBUG_BLOCK
0104     if( m_solver ) {
0105         m_solver->setAutoDelete( true );
0106         m_solver->requestAbort();
0107         m_solver = nullptr;
0108     }
0109 }
0110 
0111 void
0112 Dynamic::BiasedPlaylist::startSolver( int numRequested )
0113 {
0114     DEBUG_BLOCK
0115     debug() << "BiasedPlaylist in:" << QThread::currentThreadId();
0116 
0117     if( !m_solver )
0118     {
0119         debug() << "assigning new m_solver";
0120         m_solver = new BiasSolver( numRequested, m_bias, getContext() );
0121         connect( m_solver, &BiasSolver::done, this, &BiasedPlaylist::solverFinished );
0122 
0123         Amarok::Logger::newProgressOperation( m_solver,
0124                                                             i18n( "Generating playlist..." ), 100,
0125                                                             this, &BiasedPlaylist::requestAbort );
0126 
0127         ThreadWeaver::Queue::instance()->enqueue( QSharedPointer<ThreadWeaver::Job>(m_solver) );
0128 
0129         debug() << "called prepareToRun";
0130     }
0131     else
0132         debug() << "solver already running!";
0133 }
0134 
0135 void
0136 Dynamic::BiasedPlaylist::biasChanged()
0137 {
0138     Q_EMIT changed( this );
0139     bool inModel = DynamicModel::instance()->index( this ).isValid();
0140     if( inModel )
0141         DynamicModel::instance()->biasChanged( m_bias );
0142 }
0143 
0144 void
0145 Dynamic::BiasedPlaylist::biasReplaced( const Dynamic::BiasPtr &oldBias, const Dynamic::BiasPtr &newBias )
0146 {
0147     if( oldBias && !newBias ) // don't move the last bias away from this playlist without replacement
0148         return;
0149 
0150     bool inModel = DynamicModel::instance()->index( this ).isValid();
0151     if( m_bias )
0152     {
0153         disconnect( m_bias.data(), nullptr, this, nullptr );
0154 
0155         if( inModel )
0156             Dynamic::DynamicModel::instance()->beginRemoveBias( this );
0157         m_bias = nullptr;
0158         if( inModel )
0159             Dynamic::DynamicModel::instance()->endRemoveBias();
0160     }
0161 
0162     if( inModel )
0163         Dynamic::DynamicModel::instance()->beginInsertBias( this );
0164     m_bias = newBias;
0165     if( inModel )
0166         Dynamic::DynamicModel::instance()->endInsertBias();
0167 
0168     connect( m_bias.data(), &AbstractBias::changed,
0169              this, &BiasedPlaylist::biasChanged );
0170     connect( m_bias.data(), &AbstractBias::replaced,
0171              this, &BiasedPlaylist::biasReplaced );
0172 
0173     if( oldBias ) // don't Q_EMIT a changed during construction
0174         biasChanged();
0175 }
0176 
0177 void
0178 Dynamic::BiasedPlaylist::requestTracks( int n )
0179 {
0180     if( n > 0 )
0181         startSolver( n + 1 ); // we request an additional track so that we don't end up in a position that e.g. does have no "similar" track.
0182 }
0183 
0184 Dynamic::BiasPtr
0185 Dynamic::BiasedPlaylist::bias() const
0186 {
0187     return m_bias;
0188 }
0189 
0190 void
0191 Dynamic::BiasedPlaylist::solverFinished()
0192 {
0193     DEBUG_BLOCK
0194 
0195     if( m_solver != sender() )
0196         return; // maybe an old solver... aborted solvers should autodelete
0197 
0198     Meta::TrackList list = m_solver->solution();
0199     if( list.count() > 0 )
0200     {
0201         // remove the additional requested track
0202         if( list.count() > 1 )
0203             list.removeLast();
0204         Q_EMIT tracksReady( list );
0205     }
0206 
0207     m_solver->deleteLater();
0208     m_solver = nullptr;
0209 }
0210 
0211 
0212 Meta::TrackList
0213 Dynamic::BiasedPlaylist::getContext()
0214 {
0215     Meta::TrackList context = The::playlist()->tracks();
0216 
0217     return context;
0218 }
0219