mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-13 17:43:59 +02:00
Found a better place to do bundle loading, and also load metadata.
This commit is contained in:
@@ -451,7 +451,7 @@ SettingsDialog::openAccountConfig( Account* account, bool showDelete )
|
|||||||
void
|
void
|
||||||
SettingsDialog::installFromFile()
|
SettingsDialog::installFromFile()
|
||||||
{
|
{
|
||||||
QString resolver = QFileDialog::getOpenFileName( 0, tr( "Install resolver from file" ),
|
const QString resolver = QFileDialog::getOpenFileName( 0, tr( "Install resolver from file" ),
|
||||||
TomahawkSettings::instance()->scriptDefaultPath(),
|
TomahawkSettings::instance()->scriptDefaultPath(),
|
||||||
tr( "Tomahawk Resolvers (*.axe *.js);;"
|
tr( "Tomahawk Resolvers (*.axe *.js);;"
|
||||||
"All files (*)" ),
|
"All files (*)" ),
|
||||||
@@ -460,39 +460,9 @@ SettingsDialog::installFromFile()
|
|||||||
|
|
||||||
if( !resolver.isEmpty() )
|
if( !resolver.isEmpty() )
|
||||||
{
|
{
|
||||||
//I'm still a little proof of concept, refactor me into a proper class
|
const QFileInfo resolverAbsoluteFilePath( resolver );
|
||||||
QFileInfo resolverAbsoluteFilePath( resolver );
|
|
||||||
TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() );
|
TomahawkSettings::instance()->setScriptDefaultPath( resolverAbsoluteFilePath.absolutePath() );
|
||||||
|
|
||||||
if ( resolverAbsoluteFilePath.suffix() == "axe" )
|
|
||||||
{
|
|
||||||
QDir dir( TomahawkUtils::extractScriptPayload( resolverAbsoluteFilePath.filePath(),
|
|
||||||
resolverAbsoluteFilePath.baseName(),
|
|
||||||
"manualresolvers" ) );
|
|
||||||
dir.cd( "content" );
|
|
||||||
QFile desktopFile( dir.absoluteFilePath( "metadata.desktop" ) );
|
|
||||||
if ( desktopFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
|
|
||||||
{
|
|
||||||
QTextStream desktopFileStream( &desktopFile );
|
|
||||||
|
|
||||||
while ( !desktopFileStream.atEnd() )
|
|
||||||
{
|
|
||||||
QString line = desktopFileStream.readLine().trimmed();
|
|
||||||
//TODO: correct baseName
|
|
||||||
if ( line.startsWith( "X-Synchrotron-MainScript" ) )
|
|
||||||
{
|
|
||||||
line.remove( QRegExp( "^X-Synchrotron-MainScript\\s*=\\s*" ) );
|
|
||||||
resolver = dir.absoluteFilePath( line ); //this is our path to the JS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolverAbsoluteFilePath = QFileInfo( resolver );
|
|
||||||
|
|
||||||
//TODO: if the new path has a metadata file in the dir structure, read it, check the config
|
|
||||||
// and act accordingly for multi-account resolvers
|
|
||||||
|
|
||||||
|
|
||||||
if ( resolverAbsoluteFilePath.baseName() == "spotify_tomahawkresolver" )
|
if ( resolverAbsoluteFilePath.baseName() == "spotify_tomahawkresolver" )
|
||||||
{
|
{
|
||||||
// HACK if this is a spotify resolver, we treat it specially.
|
// HACK if this is a spotify resolver, we treat it specially.
|
||||||
|
@@ -88,6 +88,8 @@ public:
|
|||||||
virtual QWidget* aclWidget() = 0;
|
virtual QWidget* aclWidget() = 0;
|
||||||
virtual QPixmap icon() const = 0;
|
virtual QPixmap icon() const = 0;
|
||||||
#endif
|
#endif
|
||||||
|
virtual QString description() const { return QString(); }
|
||||||
|
virtual QString author() const { return QString(); }
|
||||||
|
|
||||||
virtual void saveConfig() {} // called when the widget has been edited. save values from config widget, call sync() to write to disk account generic settings
|
virtual void saveConfig() {} // called when the widget has been edited. save values from config widget, call sync() to write to disk account generic settings
|
||||||
|
|
||||||
|
@@ -278,11 +278,16 @@ AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option,
|
|||||||
// Draw the title and description
|
// Draw the title and description
|
||||||
// title
|
// title
|
||||||
QString title = index.data( Qt::DisplayRole ).toString();
|
QString title = index.data( Qt::DisplayRole ).toString();
|
||||||
|
QString author = index.data( AccountModel::AuthorRole ).toString();
|
||||||
|
QString desc = index.data( AccountModel::DescriptionRole ).toString();
|
||||||
|
|
||||||
const int rightTitleEdge = rightEdge - PADDING;
|
const int rightTitleEdge = rightEdge - PADDING;
|
||||||
const int leftTitleEdge = pixmapRect.right() + PADDING;
|
const int leftTitleEdge = pixmapRect.right() + PADDING;
|
||||||
painter->setFont( titleFont );
|
painter->setFont( titleFont );
|
||||||
QRect textRect;
|
QRect textRect;
|
||||||
const bool canRate = index.data( AccountModel::CanRateRole ).toBool();
|
const bool canRate = index.data( AccountModel::CanRateRole ).toBool()
|
||||||
|
|| !author.isEmpty()
|
||||||
|
|| !desc.isEmpty(); // if it's Attica, or it has non-empty at least author or description
|
||||||
if ( canRate )
|
if ( canRate )
|
||||||
{
|
{
|
||||||
textRect = QRect( leftTitleEdge, opt.rect.top() + PADDING, rightTitleEdge - leftTitleEdge, painter->fontMetrics().height() );
|
textRect = QRect( leftTitleEdge, opt.rect.top() + PADDING, rightTitleEdge - leftTitleEdge, painter->fontMetrics().height() );
|
||||||
@@ -294,7 +299,6 @@ AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option,
|
|||||||
painter->drawText( textRect, Qt::AlignVCenter | Qt::AlignLeft, title );
|
painter->drawText( textRect, Qt::AlignVCenter | Qt::AlignLeft, title );
|
||||||
|
|
||||||
// author
|
// author
|
||||||
QString author = index.data( AccountModel::AuthorRole ).toString();
|
|
||||||
int runningBottom = textRect.bottom();
|
int runningBottom = textRect.bottom();
|
||||||
if ( !author.isEmpty() && canRate )
|
if ( !author.isEmpty() && canRate )
|
||||||
{
|
{
|
||||||
@@ -310,7 +314,6 @@ AccountDelegate::paint ( QPainter* painter, const QStyleOptionViewItem& option,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// description
|
// description
|
||||||
QString desc = index.data( AccountModel::DescriptionRole ).toString();
|
|
||||||
const int descWidth = rightEdge - leftTitleEdge - PADDING;
|
const int descWidth = rightEdge - leftTitleEdge - PADDING;
|
||||||
painter->setFont( descFont );
|
painter->setFont( descFont );
|
||||||
const QRect descRect( leftTitleEdge, runningBottom + PADDING, descWidth, painter->fontMetrics().height() );
|
const QRect descRect( leftTitleEdge, runningBottom + PADDING, descWidth, painter->fontMetrics().height() );
|
||||||
|
@@ -337,7 +337,11 @@ AccountModel::data( const QModelIndex& index, int role ) const
|
|||||||
case Qt::DecorationRole:
|
case Qt::DecorationRole:
|
||||||
return acct->icon();
|
return acct->icon();
|
||||||
case DescriptionRole:
|
case DescriptionRole:
|
||||||
return node->factory ? node->factory->description() : QString();
|
return node->factory ?
|
||||||
|
( !node->factory->description().isEmpty() ? node->factory->description() : acct->description() )
|
||||||
|
: acct->description();
|
||||||
|
case AuthorRole:
|
||||||
|
return acct->author();
|
||||||
case Qt::CheckStateRole:
|
case Qt::CheckStateRole:
|
||||||
return acct->enabled() ? Qt::Checked : Qt::Unchecked;
|
return acct->enabled() ? Qt::Checked : Qt::Unchecked;
|
||||||
case AccountData:
|
case AccountData:
|
||||||
|
@@ -66,8 +66,84 @@ ResolverAccountFactory::createFromPath( const QString& path, const QString& fact
|
|||||||
QFileInfo info( path );
|
QFileInfo info( path );
|
||||||
return new AtticaResolverAccount( generateId( factory ), path, info.baseName() );
|
return new AtticaResolverAccount( generateId( factory ), path, info.baseName() );
|
||||||
}
|
}
|
||||||
else
|
else //on filesystem, but it could be a bundle or a legacy resolver file
|
||||||
return new ResolverAccount( generateId( factory ), path );
|
{
|
||||||
|
QString realPath( path );
|
||||||
|
const QFileInfo pathInfo( path );
|
||||||
|
|
||||||
|
QVariantHash configuration;
|
||||||
|
|
||||||
|
if ( pathInfo.suffix() == "axe" )
|
||||||
|
{
|
||||||
|
QDir dir( TomahawkUtils::extractScriptPayload( pathInfo.filePath(),
|
||||||
|
pathInfo.baseName(),
|
||||||
|
"manualresolvers" ) );
|
||||||
|
if ( !( dir.exists() && dir.isReadable() ) ) //decompression fubar
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ( !dir.cd( "content" ) ) //more fubar
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QString desktopFilePath = dir.absoluteFilePath( "metadata.desktop" );
|
||||||
|
|
||||||
|
configuration = metadataFromDesktopFile( desktopFilePath );
|
||||||
|
realPath = configuration[ "path" ].toString();
|
||||||
|
if ( realPath.isEmpty() )
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else //either legacy resolver or uncompressed bundle, so we look for a metadata file
|
||||||
|
{
|
||||||
|
QDir dir = pathInfo.absoluteDir();//assume we are in the code directory of a bundle
|
||||||
|
if ( dir.cdUp() && dir.cdUp() ) //go up twice to the content dir, if any
|
||||||
|
{
|
||||||
|
QString desktopFilePath = dir.absoluteFilePath( "metadata.desktop" );
|
||||||
|
configuration = metadataFromDesktopFile( desktopFilePath );
|
||||||
|
configuration[ "path" ] = realPath; //our initial path still overrides whatever the desktop file says
|
||||||
|
}
|
||||||
|
//else we just have empty metadata (legacy resolver without desktop file)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: handle multi-account resolvers
|
||||||
|
|
||||||
|
return new ResolverAccount( generateId( factory ), realPath, configuration );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QVariantHash
|
||||||
|
ResolverAccountFactory::metadataFromDesktopFile( const QString& path )
|
||||||
|
{
|
||||||
|
QVariantHash result;
|
||||||
|
QFile desktopFile( path );
|
||||||
|
if ( desktopFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
|
||||||
|
{
|
||||||
|
QTextStream desktopFileStream( &desktopFile );
|
||||||
|
|
||||||
|
while ( !desktopFileStream.atEnd() )
|
||||||
|
{
|
||||||
|
QString line = desktopFileStream.readLine().trimmed();
|
||||||
|
|
||||||
|
if ( line.contains( QRegExp( "^X-Synchrotron-MainScript\\s*=\\s*" ) ) )
|
||||||
|
{
|
||||||
|
line.remove( QRegExp( "^X-Synchrotron-MainScript\\s*=\\s*" ) );
|
||||||
|
QFileInfo fi( path );
|
||||||
|
result[ "path" ] = fi.absoluteDir().absoluteFilePath( line ); //this is our path to the JS
|
||||||
|
}
|
||||||
|
else if ( line.contains( QRegExp( "^X-KDE-PluginInfo-Author\\s*=\\s*" ) ) )
|
||||||
|
{
|
||||||
|
line.remove( QRegExp( "^X-KDE-PluginInfo-Author\\s*=\\s*" ) );
|
||||||
|
result[ "author" ] = line;
|
||||||
|
}
|
||||||
|
else if ( line.contains( QRegExp( "^Comment\\s*=\\s*" ) ) )
|
||||||
|
{
|
||||||
|
line.remove( QRegExp( "^Comment\\s*=\\s*" ) );
|
||||||
|
result[ "description" ] = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: correct baseName and rename directory maybe?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -84,10 +160,10 @@ ResolverAccount::ResolverAccount( const QString& accountId )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ResolverAccount::ResolverAccount( const QString& accountId, const QString& path )
|
ResolverAccount::ResolverAccount( const QString& accountId, const QString& path, const QVariantHash& initialConfiguration )
|
||||||
: Account( accountId )
|
: Account( accountId )
|
||||||
{
|
{
|
||||||
QVariantHash configuration;
|
QVariantHash configuration( initialConfiguration );
|
||||||
configuration[ "path" ] = path;
|
configuration[ "path" ] = path;
|
||||||
setConfiguration( configuration );
|
setConfiguration( configuration );
|
||||||
|
|
||||||
@@ -242,6 +318,21 @@ ResolverAccount::icon() const
|
|||||||
return m_resolver.data()->icon();
|
return m_resolver.data()->icon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString
|
||||||
|
ResolverAccount::description() const
|
||||||
|
{
|
||||||
|
return configuration().value( "description" ).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
QString
|
||||||
|
ResolverAccount::author() const
|
||||||
|
{
|
||||||
|
return configuration().value( "author" ).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// AtticaResolverAccount
|
/// AtticaResolverAccount
|
||||||
|
|
||||||
AtticaResolverAccount::AtticaResolverAccount( const QString& accountId )
|
AtticaResolverAccount::AtticaResolverAccount( const QString& accountId )
|
||||||
|
@@ -51,6 +51,9 @@ public:
|
|||||||
|
|
||||||
// Internal use
|
// Internal use
|
||||||
static Account* createFromPath( const QString& path, const QString& factoryId, bool isAttica );
|
static Account* createFromPath( const QString& path, const QString& factoryId, bool isAttica );
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QVariantHash metadataFromDesktopFile( const QString& path );
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,6 +83,8 @@ public:
|
|||||||
QString path() const;
|
QString path() const;
|
||||||
|
|
||||||
virtual QPixmap icon() const;
|
virtual QPixmap icon() const;
|
||||||
|
virtual QString description() const;
|
||||||
|
virtual QString author() const;
|
||||||
|
|
||||||
// Not relevant
|
// Not relevant
|
||||||
virtual SipPlugin* sipPlugin() { return 0; }
|
virtual SipPlugin* sipPlugin() { return 0; }
|
||||||
@@ -91,8 +96,7 @@ private slots:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Created by factory, when user installs a new resolver
|
// Created by factory, when user installs a new resolver
|
||||||
ResolverAccount( const QString& accountId, const QString& path );
|
ResolverAccount( const QString& accountId, const QString& path, const QVariantHash& initialConfiguration = QVariantHash() );
|
||||||
|
|
||||||
void hookupResolver();
|
void hookupResolver();
|
||||||
|
|
||||||
QPointer<ExternalResolverGui> m_resolver;
|
QPointer<ExternalResolverGui> m_resolver;
|
||||||
|
Reference in New Issue
Block a user