File indexing completed on 2024-05-05 04:48:47
0001 /**************************************************************************************** 0002 * Copyright (c) 2008-2010 Soren Harward <stharward@gmail.com> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify it under * 0005 * the terms of the GNU General Public License as published by the Free Software * 0006 * Foundation; either version 2 of the License, or (at your option) any later * 0007 * version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, but WITHOUT ANY * 0010 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * 0011 * PARTICULAR PURPOSE. See the GNU General Public License for more details. * 0012 * * 0013 * You should have received a copy of the GNU General Public License along with * 0014 * this program. If not, see <http://www.gnu.org/licenses/>. * 0015 ****************************************************************************************/ 0016 0017 #define DEBUG_PREFIX "APG::Preset" 0018 0019 #include "Preset.h" 0020 0021 #include "ConstraintNode.h" 0022 #include "ConstraintFactory.h" 0023 #include "ConstraintSolver.h" 0024 #include "constraints/TrackSpreader.h" 0025 0026 #include "core/logger/Logger.h" 0027 #include "core/meta/Meta.h" 0028 #include "core/support/Components.h" 0029 #include "core/support/Debug.h" 0030 #include "core-impl/collections/support/CollectionManager.h" 0031 #include "playlist/PlaylistController.h" 0032 0033 #include <KLocalizedString> 0034 0035 #include <QDomElement> 0036 0037 #include <ThreadWeaver/Queue> 0038 0039 APG::PresetPtr 0040 APG::Preset::createFromXml( QDomElement& xmlelem ) 0041 { 0042 DEBUG_BLOCK 0043 0044 if ( xmlelem.isNull() ) { 0045 PresetPtr t( new Preset( i18n("New playlist preset") ) ); 0046 return t; 0047 } else { 0048 PresetPtr t( new Preset( i18n("Unnamed playlist preset"), xmlelem ) ); 0049 return t; 0050 } 0051 } 0052 0053 APG::PresetPtr 0054 APG::Preset::createNew() 0055 { 0056 DEBUG_BLOCK 0057 0058 PresetPtr t( new Preset( i18n("New playlist preset") ) ); 0059 return t; 0060 } 0061 0062 APG::Preset::Preset( const QString& title, QDomElement& xmlelem ) 0063 : m_title( title ) 0064 , m_constraintTreeRoot( nullptr ) 0065 { 0066 0067 if ( xmlelem.hasAttribute( QStringLiteral("title") ) ) { 0068 m_title = xmlelem.attribute( QStringLiteral("title") ); 0069 } else { 0070 m_title = i18n("Unnamed playlist preset"); 0071 } 0072 for ( int i = 0; i < xmlelem.childNodes().count(); i++ ) { 0073 QDomElement childXmlElem = xmlelem.childNodes().item( i ).toElement(); 0074 if ( !childXmlElem.isNull() ) { 0075 if ( childXmlElem.tagName() == QLatin1String("constrainttree") ) { 0076 m_constraintTreeRoot = ConstraintFactory::instance()->createGroup( childXmlElem, nullptr ); 0077 } else { 0078 error() << "unknown child: " << childXmlElem.nodeName(); 0079 } 0080 } 0081 } 0082 0083 if ( !m_constraintTreeRoot ) { 0084 m_constraintTreeRoot = ConstraintFactory::instance()->createGroup( nullptr ); 0085 } 0086 } 0087 0088 APG::Preset::Preset( const QString& title ) 0089 : m_title( title ) 0090 { 0091 0092 m_constraintTreeRoot = ConstraintFactory::instance()->createGroup( nullptr ); 0093 } 0094 0095 APG::Preset::~Preset() 0096 { 0097 delete m_constraintTreeRoot; 0098 } 0099 0100 QDomElement* 0101 APG::Preset::toXml( QDomDocument& xmldoc ) const 0102 { 0103 QDomElement e = xmldoc.createElement( QStringLiteral("generatorpreset") ); 0104 e.setAttribute( QStringLiteral("title"), m_title ); 0105 m_constraintTreeRoot->toXml( xmldoc, e ); 0106 return new QDomElement( e ); 0107 } 0108 0109 void 0110 APG::Preset::generate( int q ) 0111 { 0112 ConstraintSolver* solver = new ConstraintSolver( m_constraintTreeRoot, q ); 0113 connect( solver, &ConstraintSolver::readyToRun, this, &Preset::queueSolver ); 0114 } 0115 0116 void APG::Preset::queueSolver() { 0117 0118 /* Workaround for a design quirk of Weavers. A Weaver will not 0119 * continuously poll queued but previously unrunnable jobs to see if they 0120 * are are runnable (and won't start them if they are), so what tends to 0121 * happen is that if a job is queued before it's ready to run, it fails the 0122 * canBeExecuted() check, and sits in the queue until something wakes up 0123 * the Weaver (eg, queueing another job). Eventually, you get a pileup of 0124 * obsolete jobs in the queue, and those that do run properly return 0125 * obsolete results. So to avoid that problem, we avoid queueing the job 0126 * until it's ready to run, and then the Weaver will start running it 0127 * pretty much immediately. -- sth */ 0128 0129 Q_EMIT lock( true ); 0130 0131 ConstraintSolver* s = static_cast<ConstraintSolver*>( sender() ); 0132 Amarok::Logger::newProgressOperation( s, i18n("Generating a new playlist"), s->iterationCount(), s, &ConstraintSolver::requestAbort, Qt::QueuedConnection ); 0133 connect( s, &APG::ConstraintSolver::done, this, &Preset::solverFinished, Qt::QueuedConnection ); 0134 0135 m_constraintTreeRoot->addChild( ConstraintTypes::TrackSpreader::createNew( m_constraintTreeRoot ), 0 ); // private mandatory constraint 0136 0137 ThreadWeaver::Queue::instance()->enqueue( QSharedPointer<ThreadWeaver::Job>(s) ); 0138 } 0139 0140 void 0141 APG::Preset::solverFinished( ThreadWeaver::JobPointer job ) 0142 { 0143 m_constraintTreeRoot->removeChild( 0 ); // remove the TrackSpreader 0144 0145 ConstraintSolver* solver = static_cast<ConstraintSolver*>( job.data() ); 0146 if ( job->success() ) { 0147 debug() << "Solver" << solver->serial() << "finished successfully"; 0148 if ( !solver->satisfied() ) { 0149 Amarok::Logger::longMessage( 0150 i18n("The playlist generator created a playlist which does not meet all " 0151 "of your constraints. If you are not satisfied with the results, " 0152 "try loosening or removing some constraints and then generating a " 0153 "new playlist.") ); 0154 } 0155 The::playlistController()->insertOptioned( solver->getSolution(), Playlist::OnReplacePlaylistAction ); 0156 } else { 0157 debug() << "Ignoring results from aborted Solver" << solver->serial(); 0158 } 0159 0160 Q_EMIT lock( false ); 0161 }