File indexing completed on 2025-02-23 04:28:33
0001 /**************************************************************************************** 0002 * Copyright (c) 2013 Anmol Ahuja <darthcodus@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 "ScriptableBias" 0018 0019 #include "ScriptableBiasExporter.h" 0020 0021 #include "core/support/Debug.h" 0022 #include "core/meta/Meta.h" 0023 #include "core-impl/collections/support/CollectionManager.h" 0024 0025 #include <QApplication> 0026 #include <QCoreApplication> 0027 #include <QJSEngine> 0028 #include <QQmlEngine> 0029 #include <QThread> 0030 #include <QXmlStreamReader> 0031 #include <QXmlStreamWriter> 0032 0033 using namespace AmarokScript; 0034 0035 ScriptableBiasFactoryWrapper::ScriptableBiasFactoryWrapper(QJSEngine *engine) 0036 : QObject( engine ) 0037 , m_engine( engine ) 0038 { 0039 } 0040 0041 QJSValue 0042 ScriptableBiasFactoryWrapper::biasCtor() 0043 { 0044 return m_engine->newQObject( new ScriptableBiasFactory( m_engine ) ); 0045 } 0046 0047 QJSValue 0048 ScriptableBiasFactoryWrapper::groupBiasCtor() 0049 { 0050 return m_engine->newQObject( new ScriptableBiasFactory( m_engine, true ) ); 0051 } 0052 0053 ScriptableBiasFactoryWrapper* ScriptableBiasFactory::s_wrapper = nullptr; 0054 0055 void 0056 ScriptableBiasFactory::init( QJSEngine *engine ) 0057 { 0058 TrackSetExporter::init( engine ); 0059 if (s_wrapper == nullptr) 0060 s_wrapper = new ScriptableBiasFactoryWrapper( engine ); 0061 QJSValue scriptObj = engine->newQObject( s_wrapper ); 0062 engine->globalObject().setProperty( QStringLiteral("BiasFactory"), scriptObj.property("biasCtor") ); 0063 engine->globalObject().setProperty( QStringLiteral("GroupBiasFactory"), scriptObj.property("groupBiasCtor") ); 0064 } 0065 0066 ScriptableBiasFactory::ScriptableBiasFactory( QJSEngine *engine, bool groupBias ) 0067 : QObject( engine ) 0068 , m_groupBias( groupBias ) 0069 , m_engine( engine ) 0070 , m_enabled( false ) 0071 {} 0072 0073 ScriptableBiasFactory::~ScriptableBiasFactory() 0074 { 0075 Dynamic::BiasFactory::instance()->removeBiasFactory( this ); 0076 } 0077 0078 Dynamic::BiasPtr 0079 ScriptableBiasFactory::createBias() 0080 { 0081 ScriptableBias *bias; 0082 //if( m_groupBias ) 0083 // return new ScriptableGroupBias( this ); 0084 //else 0085 bias = new ScriptableBias( this ); 0086 Dynamic::BiasPtr biasPtr = Dynamic::BiasPtr( bias ); 0087 QJSValue biasObject = bias->scriptObject(); 0088 if( m_initFunction.isCallable() ) 0089 m_initFunction.callWithInstance( biasObject, QJSValueList() << biasObject ); 0090 0091 return biasPtr; 0092 } 0093 0094 QJSEngine* 0095 ScriptableBiasFactory::engine() const 0096 { 0097 return m_engine; 0098 } 0099 0100 void 0101 ScriptableBiasFactory::setEnabled( bool enabled ) 0102 { 0103 if( enabled ) 0104 { 0105 if( !m_enabled ) 0106 Dynamic::BiasFactory::instance()->registerNewBiasFactory( this ); 0107 } 0108 else 0109 Dynamic::BiasFactory::instance()->removeBiasFactory( this ); 0110 m_enabled = enabled; 0111 } 0112 0113 bool 0114 ScriptableBiasFactory::enabled() const 0115 { 0116 return m_enabled; 0117 } 0118 0119 void 0120 ScriptableBiasFactory::setName( const QString &name ) 0121 { 0122 m_name = name; 0123 } 0124 0125 QString 0126 ScriptableBiasFactory::name() const 0127 { 0128 return m_name; 0129 } 0130 0131 QString 0132 ScriptableBiasFactory::i18nName() const 0133 { 0134 return m_i18nName; 0135 } 0136 0137 QString 0138 ScriptableBiasFactory::i18nDescription() const 0139 { 0140 return m_description; 0141 } 0142 0143 QJSValue 0144 ScriptableBiasFactory::initFunction() const 0145 { 0146 return m_initFunction; 0147 } 0148 0149 void 0150 ScriptableBiasFactory::setInitFunction( const QJSValue &value ) 0151 { 0152 m_initFunction = value; 0153 } 0154 0155 void 0156 ScriptableBiasFactory::setI18nDescription( const QString &description ) 0157 { 0158 m_description = description; 0159 } 0160 0161 void 0162 ScriptableBiasFactory::setI18nName( const QString &i18nName ) 0163 { 0164 m_i18nName = i18nName; 0165 } 0166 0167 QJSValue 0168 ScriptableBiasFactory::widgetFunction() const 0169 { 0170 return m_widgetFunction; 0171 } 0172 0173 void 0174 ScriptableBiasFactory::setWidgetFunction( const QJSValue &value ) 0175 { 0176 // throw exception? 0177 //if( !value.isFunction() ) 0178 m_widgetFunction = value; 0179 } 0180 0181 void 0182 ScriptableBiasFactory::setFromXmlFunction( const QJSValue &value ) 0183 { 0184 m_fromXmlFunction = value; 0185 } 0186 0187 void 0188 ScriptableBiasFactory::setToXmlFunction( const QJSValue &value ) 0189 { 0190 m_toXmlFunction = value; 0191 } 0192 0193 QJSValue 0194 ScriptableBiasFactory::fromXmlFunction() const 0195 { 0196 return m_fromXmlFunction; 0197 } 0198 0199 QJSValue 0200 ScriptableBiasFactory::toXmlFunction() const 0201 { 0202 return m_toXmlFunction; 0203 } 0204 0205 QJSValue 0206 ScriptableBiasFactory::matchingTracksFunction() const 0207 { 0208 return m_matchingTracksFunction; 0209 } 0210 0211 void 0212 ScriptableBiasFactory::setMatchingTracksFunction( const QJSValue &value ) 0213 { 0214 m_matchingTracksFunction = value; 0215 } 0216 0217 void 0218 ScriptableBiasFactory::setTrackMatchesFunction( const QJSValue &value ) 0219 { 0220 m_trackMatchesFunction = value; 0221 } 0222 0223 QJSValue 0224 ScriptableBiasFactory::trackMatchesFunction() const 0225 { 0226 return m_trackMatchesFunction; 0227 } 0228 0229 void 0230 ScriptableBiasFactory::setToStringFunction( const QJSValue &value ) 0231 { 0232 m_toStringFunction = value; 0233 } 0234 0235 QJSValue 0236 ScriptableBiasFactory::toStringFunction() const 0237 { 0238 return m_toStringFunction; 0239 } 0240 0241 /********************************************************************************* 0242 // ScriptableBias 0243 **********************************************************************************/ 0244 void 0245 ScriptableBias::toXml( QXmlStreamWriter *writer ) const 0246 { 0247 if( m_scriptBias->toXmlFunction().isCallable() ) 0248 m_scriptBias->fromXmlFunction().callWithInstance( m_biasObject, 0249 QJSValueList() << m_engine->toScriptValue<QXmlStreamWriter*>( writer ) ); 0250 else 0251 Dynamic::AbstractBias::toXml( writer ); 0252 } 0253 0254 void 0255 ScriptableBias::fromXml( QXmlStreamReader *reader ) 0256 { 0257 if( m_scriptBias->fromXmlFunction().isCallable() ) 0258 m_scriptBias->fromXmlFunction().callWithInstance( m_biasObject, 0259 QJSValueList() << m_engine->toScriptValue<QXmlStreamReader*>( reader ) ); 0260 else 0261 Dynamic::AbstractBias::fromXml( reader ); 0262 } 0263 0264 QWidget* 0265 ScriptableBias::widget( QWidget *parent ) 0266 { 0267 QWidget *widget = dynamic_cast<QWidget*>( m_scriptBias->widgetFunction().callWithInstance( m_biasObject, 0268 QJSValueList() << m_scriptBias->engine()->newQObject( parent ) ).toQObject() ); 0269 if( widget ) 0270 return widget; 0271 return Dynamic::AbstractBias::widget( parent ); 0272 } 0273 0274 void 0275 ScriptableBias::invalidate() 0276 { 0277 Dynamic::AbstractBias::invalidate(); 0278 } 0279 0280 Dynamic::TrackSet 0281 ScriptableBias::matchingTracks(const Meta::TrackList &playlist, int contextCount, int finalCount, const Dynamic::TrackCollectionPtr &universe ) const 0282 { 0283 DEBUG_BLOCK 0284 if( QThread::currentThread() == QCoreApplication::instance()->thread() ) 0285 return slotMatchingTracks( playlist, contextCount, finalCount, universe ); 0286 0287 Dynamic::TrackSet retVal; 0288 Q_ASSERT( QMetaObject::invokeMethod( const_cast<ScriptableBias*>( this ), "slotMatchingTracks", Qt::BlockingQueuedConnection, 0289 Q_RETURN_ARG( Dynamic::TrackSet, retVal), 0290 Q_ARG( Meta::TrackList, playlist ), 0291 Q_ARG( int, contextCount ), 0292 Q_ARG( int, finalCount ), 0293 Q_ARG( Dynamic::TrackCollectionPtr, universe ) 0294 ) ); 0295 debug() << "Returning trackSet, trackCount " << retVal.trackCount() << ", isOutstanding " << retVal.isOutstanding(); 0296 return retVal; 0297 } 0298 0299 Dynamic::TrackSet 0300 ScriptableBias::slotMatchingTracks( const Meta::TrackList &playlist, int contextCount, int finalCount, const Dynamic::TrackCollectionPtr &universe ) const 0301 { 0302 Q_ASSERT( QThread::currentThread() == QCoreApplication::instance()->thread() ); 0303 if( m_scriptBias->matchingTracksFunction().isCallable() ) 0304 { 0305 QJSValue trackSetVal = m_scriptBias->matchingTracksFunction().callWithInstance( m_biasObject, 0306 QJSValueList() << m_engine->toScriptValue<Meta::TrackList>( playlist ) 0307 << contextCount 0308 << finalCount 0309 << m_engine->toScriptValue<QStringList>( universe->uids() ) ); 0310 TrackSetExporter *trackSetExporter = dynamic_cast<TrackSetExporter*>( trackSetVal.toQObject() ); 0311 if( trackSetExporter ) 0312 return Dynamic::TrackSet( *trackSetExporter ); 0313 } 0314 debug() << "Invalid trackSet received"; 0315 return Dynamic::TrackSet( universe, false ); 0316 } 0317 0318 QString 0319 ScriptableBias::name() const 0320 { 0321 QString name; 0322 if( m_scriptBias ) 0323 name = m_scriptBias->name(); 0324 return name.isEmpty() ? Dynamic::AbstractBias::name() : name; 0325 } 0326 0327 void 0328 ScriptableBias::ready( const Dynamic::TrackSet &trackSet ) 0329 { 0330 debug() << "Received trackset, count: " << trackSet.trackCount() << "Is outstanding:" << trackSet.isOutstanding(); 0331 Q_EMIT resultReady( trackSet ); 0332 } 0333 0334 void 0335 ScriptableBias::paintOperator( QPainter *painter, const QRect &rect, Dynamic::AbstractBias *bias ) 0336 { 0337 Dynamic::AbstractBias::paintOperator( painter, rect, bias ); 0338 } 0339 0340 void 0341 ScriptableBias::replace(const Dynamic::BiasPtr &newBias ) 0342 { 0343 Dynamic::AbstractBias::replace( newBias ); 0344 } 0345 0346 QString 0347 ScriptableBias::toString() const 0348 { 0349 return m_scriptBias->toStringFunction().call( QJSValueList() << m_biasObject ).toString(); 0350 } 0351 0352 bool 0353 ScriptableBias::trackMatches( int position, const Meta::TrackList& playlist, int contextCount ) const 0354 { 0355 if( m_scriptBias->trackMatchesFunction().isCallable() ) 0356 return m_scriptBias->trackMatchesFunction().callWithInstance( m_biasObject, 0357 QJSValueList() << position 0358 << m_engine->toScriptValue<Meta::TrackList>( playlist ) 0359 << contextCount 0360 ).toBool(); 0361 return true; 0362 } 0363 0364 ScriptableBias::ScriptableBias( ScriptableBiasFactory *biasProto ) 0365 : m_scriptBias( biasProto ) 0366 , m_engine( biasProto->engine() ) 0367 { 0368 m_biasObject = m_engine->newQObject( this ); 0369 QQmlEngine::setObjectOwnership( this, QQmlEngine::CppOwnership ); 0370 connect( m_engine, &QObject::destroyed, this, &ScriptableBias::removeBias ); 0371 } 0372 0373 ScriptableBias::~ScriptableBias() 0374 {} 0375 0376 void 0377 ScriptableBias::removeBias() 0378 { 0379 replace( Dynamic::BiasPtr( new Dynamic::ReplacementBias( name() ) ) ); 0380 } 0381 0382 ///////////////////////////////////////////////////////////////////////////////////////// 0383 // TrackSetExporterWrapper 0384 ///////////////////////////////////////////////////////////////////////////////////////// 0385 0386 TrackSetExporterWrapper *TrackSetExporter::s_wrapper = nullptr; 0387 TrackSetExporterWrapper::TrackSetExporterWrapper( QJSEngine* engine ) 0388 : QObject( engine ) 0389 , m_engine (engine ) 0390 { 0391 } 0392 QJSValue 0393 TrackSetExporterWrapper::trackSetConstructor( QJSValue arg0, QJSValue arg1 ) 0394 { 0395 DEBUG_BLOCK 0396 0397 TrackSetExporter *trackSetExporter = nullptr; 0398 // Called with 1 argument 0399 if ( !arg0.isUndefined() && arg1.isUndefined() ) 0400 { 0401 TrackSetExporter *trackSetPrototype = dynamic_cast<TrackSetExporter*>( arg0.toQObject() ); 0402 if( trackSetPrototype ) { 0403 trackSetExporter = new TrackSetExporter( Dynamic::TrackSet( *trackSetPrototype ) ); 0404 } 0405 // Called with 2 arguments 0406 } else if ( !arg0.isUndefined() && arg1.isBool() ) { 0407 bool isFull = arg1.toBool(); 0408 QStringList uidList; 0409 Meta::TrackList trackList; 0410 if( arg0.toVariant().canConvert<QStringList>() ) 0411 { 0412 uidList = arg0.toVariant().toStringList(); 0413 Q_ASSERT( !arg0.toVariant().canConvert<Meta::TrackList>() ); 0414 trackSetExporter = new TrackSetExporter( Dynamic::TrackSet( Dynamic::TrackCollectionPtr( new Dynamic::TrackCollection( uidList ) ), isFull ) ); 0415 } 0416 else if( arg0.toVariant().canConvert<Meta::TrackList>() ) 0417 { 0418 debug() << "In Meta::Tracklist TrackSet ctor"; 0419 trackList = qjsvalue_cast<Meta::TrackList>( arg0 ); 0420 foreach( const Meta::TrackPtr &track, trackList ) 0421 { 0422 if( track ) 0423 uidList << track->uidUrl(); 0424 } 0425 trackSetExporter = new TrackSetExporter( Dynamic::TrackSet( Dynamic::TrackCollectionPtr( new Dynamic::TrackCollection( uidList ) ), isFull ) ); 0426 } 0427 } 0428 if( trackSetExporter == nullptr ) 0429 { 0430 /* TODO - Use commented code once QT versions >= 5.12 0431 m_engine->throwError( QJSValue::SyntaxError, QStringLiteral("Invalid arguments for TrackSet!") ); 0432 return QJSValue(QJSValue::UndefinedValue); 0433 */ 0434 return m_engine->evaluate("throw new TypeError('Invalid arguments for TrackSet!')"); 0435 } 0436 0437 const QJSValue trackSetObject = m_engine->newQObject( trackSetExporter ); 0438 return trackSetObject; 0439 } 0440 0441 ///////////////////////////////////////////////////////////////////////////////////////// 0442 // TrackSetExporter 0443 ///////////////////////////////////////////////////////////////////////////////////////// 0444 0445 void 0446 TrackSetExporter::init( QJSEngine *engine ) 0447 { 0448 qRegisterMetaType<Dynamic::TrackSet>(); 0449 QMetaType::registerConverter<Dynamic::TrackSet, QJSValue>( [=] (Dynamic::TrackSet trackSet) { return toScriptValue( engine, trackSet ); } ); 0450 QMetaType::registerConverter<QJSValue, Dynamic::TrackSet>( [] (QJSValue jsValue) { 0451 Dynamic::TrackSet trackSet; 0452 fromScriptValue( jsValue, trackSet ); 0453 return trackSet; 0454 } ); 0455 if (s_wrapper == nullptr) 0456 s_wrapper = new TrackSetExporterWrapper( engine ); 0457 engine->globalObject().setProperty( QStringLiteral("TrackSet"), engine->newQObject( s_wrapper ).property( "trackSetConstructor" ) ); 0458 } 0459 0460 void 0461 TrackSetExporter::fromScriptValue( const QJSValue &obj, Dynamic::TrackSet &trackSet ) 0462 { 0463 DEBUG_BLOCK 0464 TrackSetExporter *trackSetProto = dynamic_cast<TrackSetExporter*>( obj.toQObject() ); 0465 if( !trackSetProto ) 0466 trackSet = Dynamic::TrackSet( Dynamic::TrackCollectionPtr( new Dynamic::TrackCollection( QStringList() ) ), false ); 0467 else 0468 trackSet = *trackSetProto; 0469 } 0470 0471 QJSValue 0472 TrackSetExporter::toScriptValue( QJSEngine *engine, const Dynamic::TrackSet &trackSet ) 0473 { 0474 DEBUG_BLOCK 0475 TrackSetExporter *trackProto = new TrackSetExporter( trackSet ); 0476 QJSValue val = engine->newQObject( trackProto ); 0477 QQmlEngine::setObjectOwnership( trackProto, QQmlEngine::JavaScriptOwnership); 0478 return val; 0479 } 0480 0481 bool 0482 TrackSetExporter::containsUid( const QString &uid ) const 0483 { 0484 return Dynamic::TrackSet::contains( uid ); 0485 } 0486 0487 0488 void 0489 TrackSetExporter::reset( bool value ) 0490 { 0491 Dynamic::TrackSet::reset( value ); 0492 } 0493 0494 void 0495 TrackSetExporter::intersectTrackSet( const Dynamic::TrackSet &trackSet) 0496 { 0497 Dynamic::TrackSet::intersect( trackSet ); 0498 } 0499 0500 void 0501 TrackSetExporter::intersectUids( const QStringList &uids ) 0502 { 0503 Dynamic::TrackSet::intersect( uids ); 0504 } 0505 0506 void 0507 TrackSetExporter::subtractTrack( const Meta::TrackPtr &track ) 0508 { 0509 Dynamic::TrackSet::subtract( track ); 0510 } 0511 0512 void 0513 TrackSetExporter::subtractTrackSet( const Dynamic::TrackSet &trackSet ) 0514 { 0515 Dynamic::TrackSet::subtract( trackSet ); 0516 } 0517 0518 void 0519 TrackSetExporter::subtractUids( const QStringList &uids ) 0520 { 0521 Dynamic::TrackSet::subtract( uids ); 0522 } 0523 0524 void 0525 TrackSetExporter::uniteTrack( const Meta::TrackPtr &track ) 0526 { 0527 Dynamic::TrackSet::unite( track ); 0528 } 0529 0530 void 0531 TrackSetExporter::uniteTrackSet( const Dynamic::TrackSet &trackSet ) 0532 { 0533 Dynamic::TrackSet::unite( trackSet ); 0534 } 0535 0536 void 0537 TrackSetExporter::uniteUids( const QStringList &uids ) 0538 { 0539 Dynamic::TrackSet::unite( uids ); 0540 } 0541 0542 Meta::TrackPtr 0543 TrackSetExporter::getRandomTrack() const 0544 { 0545 return CollectionManager::instance()->trackForUrl( QUrl( Dynamic::TrackSet::getRandomTrack() ) ); 0546 } 0547 0548 bool 0549 TrackSetExporter::containsTrack( const Meta::TrackPtr &track ) const 0550 { 0551 return Dynamic::TrackSet::contains( track ); 0552 } 0553 0554 // private 0555 TrackSetExporter::TrackSetExporter( const Dynamic::TrackSet &trackSet ) 0556 : QObject( nullptr ) 0557 , TrackSet( trackSet ) 0558 {}