Add some basic script loading support and add python bindings for the Player class

This commit is contained in:
David Sansome 2010-12-31 18:13:28 +00:00
parent 6ea9ea3b4d
commit 5b1000834f
69 changed files with 1070 additions and 5 deletions

View File

@ -6,6 +6,7 @@ include(cmake/Summary.cmake)
include(cmake/Version.cmake)
include(cmake/Deb.cmake)
include(cmake/Rpm.cmake)
include(cmake/SipBindings.cmake)
find_package(Qt4 4.5.0 REQUIRED QtCore QtGui QtOpenGL QtSql QtNetwork QtXml)
if(UNIX AND NOT APPLE)
@ -41,6 +42,7 @@ find_package(OpenGL REQUIRED)
find_package(Boost REQUIRED)
find_package(Gettext REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(PythonLibs)
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
pkg_check_modules(GSTREAMER gstreamer-0.10)
@ -138,6 +140,8 @@ option(ENABLE_VISUALISATIONS "Use libprojectm visualisations" ON)
option(BUNDLE_PROJECTM_PRESETS "Install Clementine's own copies of libprojectm presets - disable this if you want to use a system package instead" ON)
option(ENABLE_SOUNDMENU "Add Clementine to the Gnome sound menu" ON)
option(ENABLE_LIBLASTFM "Use liblastfm for fetching song info, scrobbling and radio streams" ON)
option(ENABLE_SCRIPTING "Enable scripting support" ON)
option(ENABLE_SCRIPTING_PYTHON "Enable python scripting" ON)
if(ENABLE_LIBGPOD AND LIBGPOD_FOUND)
set(HAVE_LIBGPOD ON)
@ -163,6 +167,14 @@ if(ENABLE_LIBLASTFM AND LASTFM_LIBRARIES AND LASTFM_INCLUDE_DIRS)
set(HAVE_LIBLASTFM ON)
endif(ENABLE_LIBLASTFM AND LASTFM_LIBRARIES AND LASTFM_INCLUDE_DIRS)
if(ENABLE_SCRIPTING)
set(HAVE_SCRIPTING ON)
endif(ENABLE_SCRIPTING)
if(ENABLE_SCRIPTING_PYTHON AND ENABLE_SCRIPTING AND PYTHONLIBS_FOUND AND PYQT_SIP_DIR)
set(HAVE_SCRIPTING_PYTHON ON)
endif(ENABLE_SCRIPTING_PYTHON AND ENABLE_SCRIPTING AND PYTHONLIBS_FOUND AND PYQT_SIP_DIR)
if(ENABLE_VISUALISATIONS)
# When/if upstream accepts our patches then these options can be used to link
# to system installed projectM instead.
@ -278,5 +290,7 @@ summary_add("Gnome sound menu integration" HAVE_LIBINDICATE)
summary_add("Wiimote support" ENABLE_WIIMOTEDEV)
summary_add("Visualisations" ENABLE_VISUALISATIONS)
summary_add("Last.fm support" HAVE_LIBLASTFM)
summary_add("Scripting support" HAVE_SCRIPTING)
summary_add("Scripting support: Python" HAVE_SCRIPTING_PYTHON)
summary_add("(Mac OS X) Sparkle integration" HAVE_SPARKLE)
summary_show()

63
cmake/SipBindings.cmake Normal file
View File

@ -0,0 +1,63 @@
find_path(PYQT_SIP_DIR pyqt-gpl.sip
PATH_SUFFIXES share/sip/PyQt4
)
macro(add_sip_bindings outputvar)
# Work out what the SIP flags should be for PyQt4. These would normally be
# obtained from PyQt4.pyqtconfig.Configuration().pyqt_sip_flags, but we can't
# call that when cross-compiling.
set(PYQT_SIP_FLAGS
"-x" "VendorID"
"-x" "PyQt_NoPrintRangeBug"
"-t" "Qt_${QT_VERSION_MAJOR}_${QT_VERSION_MINOR}_${QT_VERSION_PATCH}"
)
if(WIN32)
list(APPEND PYQT_SIP_FLAGS "-t" "WS_WIN")
elseif(APPLE)
list(APPEND PYQT_SIP_FLAGS "-t" "WS_MAC")
else(WIN32)
list(APPEND PYQT_SIP_FLAGS "-t" "WS_X11")
endif(WIN32)
foreach(source ${ARGN})
get_filename_component(source_directory ${source} PATH)
get_filename_component(source_basename ${source} NAME)
get_filename_component(source_module ${source} NAME_WE)
set(outputs
"${CMAKE_CURRENT_BINARY_DIR}/sip${source_module}cmodule.cpp"
"${CMAKE_CURRENT_BINARY_DIR}/sipAPI${source_module}.h"
)
# Find any included files
execute_process(
COMMAND "awk" "/^%Include/ {ORS=\";\"; print \"${source_directory}/\" $2}"
"${CMAKE_CURRENT_SOURCE_DIR}/${source}"
OUTPUT_VARIABLE included_files
)
foreach(included_file ${included_files} ${source})
# Sip creates 1 file per class... so we have to figure out what classes
# it will generate
execute_process(
COMMAND "awk" "/^\\s*class +([A-Za-z0-9]+)/ {ORS=\";\"; print $2}"
"${CMAKE_CURRENT_SOURCE_DIR}/${included_file}"
OUTPUT_VARIABLE classes
)
foreach(class ${classes})
list(APPEND outputs "${CMAKE_CURRENT_BINARY_DIR}/sip${source_module}${class}.cpp")
endforeach(class)
endforeach(included_file)
add_custom_command(
OUTPUT ${outputs}
COMMAND "sip" ${PYQT_SIP_FLAGS}
"-I${PYQT_SIP_DIR}"
"-c" "${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/${source}"
DEPENDS ${included_files} "${source}"
)
list(APPEND ${outputvar} ${outputs})
endforeach(source)
endmacro(add_sip_bindings)

View File

@ -27,6 +27,10 @@ if(HAVE_LIBLASTFM)
include_directories(${LASTFM_INCLUDE_DIRS})
endif(HAVE_LIBLASTFM)
if(HAVE_SCRIPTING_PYTHON)
include_directories(${PYTHON_INCLUDE_DIRS})
endif(HAVE_SCRIPTING_PYTHON)
cmake_policy(SET CMP0011 NEW)
include(../cmake/ParseArguments.cmake)
include(../cmake/Translations.cmake)
@ -684,6 +688,33 @@ if(APPLE)
list(APPEND SOURCES core/mac_startup.mm)
endif(APPLE)
if(HAVE_SCRIPTING)
list(APPEND SOURCES
scripting/languageengine.cpp
scripting/script.cpp
scripting/scriptdialog.cpp
scripting/scriptmanager.cpp
)
list(APPEND HEADERS
scripting/scriptdialog.h
scripting/scriptmanager.h
)
list(APPEND UI
scripting/scriptdialog.ui
)
if(HAVE_SCRIPTING_PYTHON)
list(APPEND SOURCES
scripting/python/pythonengine.cpp
scripting/python/pythonscript.cpp
)
add_sip_bindings(SOURCES
scripting/python/clementine.sip
)
endif(HAVE_SCRIPTING_PYTHON)
endif(HAVE_SCRIPTING)
# OS-specific sources that should be searched for translatable strings even
# if they're not compiled
list(APPEND OTHER_SOURCES
@ -813,6 +844,10 @@ if(HAVE_LIBINDICATE)
target_link_libraries(clementine_lib ${INDICATEQT_LIBRARIES})
endif(HAVE_LIBINDICATE)
if(HAVE_SCRIPTING_PYTHON)
target_link_libraries(clementine_lib ${PYTHON_LIBRARIES})
endif(HAVE_SCRIPTING_PYTHON)
if (APPLE)
target_link_libraries(clementine_lib
${GROWL}

View File

@ -43,4 +43,7 @@
#cmakedefine HAVE_LIBLASTFM
#cmakedefine HAVE_LIBMTP
#cmakedefine HAVE_SCRIPTING
#cmakedefine HAVE_SCRIPTING_PYTHON
#endif // CONFIG_H_IN

View File

@ -220,6 +220,9 @@ QString GetConfigPath(ConfigPath config) {
return GetConfigPath(Path_Root) +
QString("/gst-registry-%1-bin").arg(QCoreApplication::applicationVersion());
case Path_Scripts:
return GetConfigPath(Path_Root) + "/scripts";
case Path_DefaultMusicLibrary:
#ifdef Q_OS_DARWIN
return mac::GetMusicDirectory();

View File

@ -48,6 +48,7 @@ namespace Utilities {
Path_NetworkCache,
Path_GstreamerRegistry,
Path_DefaultMusicLibrary,
Path_Scripts,
};
QString GetConfigPath(ConfigPath config);
}

View File

@ -0,0 +1,18 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "languageengine.h"

View File

@ -0,0 +1,35 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LANGUAGEENGINE_H
#define LANGUAGEENGINE_H
#include "scriptmanager.h"
class Script;
class LanguageEngine {
public:
virtual ~LanguageEngine() {}
virtual ScriptManager::Language language() const = 0;
virtual QString name() const = 0;
virtual Script* CreateScript(const QString& path, const QString& script_file) = 0;
};
#endif // LANGUAGEENGINE_H

View File

@ -0,0 +1,6 @@
%Module clementine 0
%Import QtCore/QtCoremod.sip
%Import QtGui/QtGuimod.sip
%Include player.sip

View File

@ -0,0 +1,54 @@
class Player : QObject {
%TypeHeaderCode
#include "core/player.h"
%End
public:
// Engine::State GetState() const;
int GetVolume() const;
//PlaylistItemPtr GetCurrentItem() const { return current_item_; }
//PlaylistItemPtr GetItemAt(int pos) const;
//PlaylistManager* playlists() const { return playlists_; }
public slots:
// Manual track change to the specified track
//void PlayAt(int i, Engine::TrackChangeType change, bool reshuffle);
// If there's currently a song playing, pause it, otherwise play the track
// that was playing last, or the first one on the playlist
void PlayPause();
// Skips this track. Might load more of the current radio station.
void Next();
void Previous();
void SetVolume(int value);
void VolumeUp();
void VolumeDown();
void Mute();
void Seek(int seconds);
void SeekForward();
void SeekBackward();
void Pause();
void Stop();
void Play();
void ShowOSD();
signals:
void Playing();
void Paused();
void Stopped();
void PlaylistFinished();
void VolumeChanged(int volume);
void Error(const QString& message);
//void TrackSkipped(PlaylistItemPtr old_track);
//void ForceShowOSD(Song);
private:
Player();
};

View File

@ -0,0 +1,47 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Python.h>
#include "pythonengine.h"
#include "pythonscript.h"
extern "C" {
void initclementine();
}
PythonEngine::PythonEngine()
: initialised_(false)
{
}
Script* PythonEngine::CreateScript(const QString& path, const QString& script_file) {
// Initialise Python if it hasn't been done yet
if (!initialised_) {
// Add the Clementine builtin module
PyImport_AppendInittab(const_cast<char*>("clementine"), initclementine);
Py_SetProgramName(const_cast<char*>("clementine"));
PyEval_InitThreads();
Py_InitializeEx(0);
PyEval_ReleaseLock();
initialised_ = true;
}
return new PythonScript(path, script_file);
}

View File

@ -0,0 +1,36 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PYTHONENGINE_H
#define PYTHONENGINE_H
#include "scripting/languageengine.h"
class PythonEngine : public LanguageEngine {
public:
PythonEngine();
ScriptManager::Language language() const { return ScriptManager::Language_Python; }
QString name() const { return "python"; }
Script* CreateScript(const QString& path, const QString& script_file);
private:
bool initialised_;
};
#endif // PYTHONENGINE_H

View File

@ -0,0 +1,67 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Python.h>
#include "pythonscript.h"
#include <QFile>
#include <QtDebug>
PythonScript::PythonScript(const QString& path, const QString& script_file)
: Script(path, script_file),
interpreter_(NULL)
{
}
bool PythonScript::Init() {
// Open the file
QFile file(script_file());
if (!file.open(QIODevice::ReadOnly)) {
qWarning() << "Error opening file:" << script_file();
return false;
}
// Create a python interpreter
PyEval_AcquireLock();
interpreter_ = Py_NewInterpreter();
PyEval_ReleaseLock();
// Get a file stream from the file handle
FILE* stream = fdopen(file.handle(), "r");
if (PyRun_SimpleFile(stream, script_file().toLocal8Bit().constData()) != 0) {
PyEval_AcquireLock();
Py_EndInterpreter(interpreter_);
PyEval_ReleaseLock();
interpreter_ = NULL;
return false;
}
return true;
}
bool PythonScript::Unload() {
PyEval_AcquireLock();
Py_EndInterpreter(interpreter_);
PyEval_ReleaseLock();
interpreter_ = NULL;
return true;
}

View File

@ -0,0 +1,36 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PYTHONSCRIPT_H
#define PYTHONSCRIPT_H
#include "scripting/script.h"
struct _ts; // PyThreadState
class PythonScript : public Script {
public:
PythonScript(const QString& path, const QString& script_file);
bool Init();
bool Unload();
private:
_ts* interpreter_;
};
#endif // PYTHONSCRIPT_H

24
src/scripting/script.cpp Normal file
View File

@ -0,0 +1,24 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "script.h"
Script::Script(const QString& path, const QString& script_file)
: path_(path),
script_file_(script_file)
{
}

40
src/scripting/script.h Normal file
View File

@ -0,0 +1,40 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPT_H
#define SCRIPT_H
#include <QString>
class Script {
public:
Script(const QString& path, const QString& script_file);
virtual ~Script() {}
const QString& path() const { return path_; }
const QString& script_file() const { return script_file_; }
virtual bool Init() = 0;
virtual bool Unload() = 0;
virtual bool Reload() { return Unload() && Init(); }
private:
QString path_;
QString script_file_;
};
#endif // SCRIPT_H

View File

@ -0,0 +1,37 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "scriptdialog.h"
#include "scriptmanager.h"
#include "ui_scriptdialog.h"
ScriptDialog::ScriptDialog(QWidget* parent)
: QDialog(parent),
ui_(new Ui_ScriptDialog),
manager_(NULL)
{
ui_->setupUi(this);
}
ScriptDialog::~ScriptDialog() {
delete ui_;
}
void ScriptDialog::SetManager(ScriptManager* manager) {
manager_ = manager;
ui_->list->setModel(manager);
}

View File

@ -0,0 +1,41 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTDIALOG_H
#define SCRIPTDIALOG_H
#include <QDialog>
class ScriptManager;
class Ui_ScriptDialog;
class ScriptDialog : public QDialog {
Q_OBJECT
public:
ScriptDialog(QWidget* parent = 0);
~ScriptDialog();
void SetManager(ScriptManager* manager);
private:
Ui_ScriptDialog* ui_;
ScriptManager* manager_;
};
#endif // SCRIPTDIALOG_H

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ScriptDialog</class>
<widget class="QDialog" name="ScriptDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>645</width>
<height>388</height>
</rect>
</property>
<property name="windowTitle">
<string>Script Manager</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QListView" name="list"/>
</item>
<item>
<widget class="QDialogButtonBox" name="button_box">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>button_box</sender>
<signal>accepted()</signal>
<receiver>ScriptDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>button_box</sender>
<signal>rejected()</signal>
<receiver>ScriptDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,178 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "languageengine.h"
#include "script.h"
#include "scriptmanager.h"
#include "core/utilities.h"
#ifdef HAVE_SCRIPTING_PYTHON
# include "scripting/python/pythonengine.h"
#endif
#include <QDirIterator>
#include <QSettings>
#include <QtDebug>
const char* ScriptManager::kIniFileName = "script.ini";
ScriptManager::ScriptManager(QObject* parent)
: QAbstractListModel(parent)
{
#ifdef HAVE_SCRIPTING_PYTHON
engines_ << new PythonEngine;
#endif
search_paths_ << Utilities::GetConfigPath(Utilities::Path_Scripts);
Reset();
}
ScriptManager::~ScriptManager() {
qDeleteAll(engines_);
}
void ScriptManager::Reset() {
// Remove any scripts that aren't loaded
for (int i=0 ; i<info_.count() ; ++i) {
if (!info_[i].loaded_) {
info_.removeAt(i);
--i;
}
}
// Search for scripts
foreach (const QString& search_path, search_paths_) {
QDirIterator it(search_path,
QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable | QDir::Executable,
QDirIterator::FollowSymlinks);
while (it.hasNext()) {
it.next();
const QString path = it.filePath();
ScriptInfo info = LoadScriptInfo(path);
if (!info.is_valid()) {
qWarning() << "Not a valid Clementine script directory, ignoring:"
<< path;
continue;
}
info_ << info;
}
}
reset();
}
LanguageEngine* ScriptManager::EngineForLanguage(const QString& language_name) const {
foreach (LanguageEngine* engine, engines_) {
if (engine->name() == language_name) {
return engine;
}
}
return NULL;
}
LanguageEngine* ScriptManager::EngineForLanguage(ScriptManager::Language language) const {
foreach (LanguageEngine* engine, engines_) {
if (engine->language() == language) {
return engine;
}
}
return NULL;
}
ScriptManager::ScriptInfo ScriptManager::LoadScriptInfo(const QString& path) {
const QString ini_file = path + "/" + kIniFileName;
// Does the file exist?
ScriptManager::ScriptInfo ret;
if (!QFile::exists(ini_file)) {
qWarning() << "Script definition file not found:" << ini_file;
return ret;
}
// Open it
QSettings s(ini_file, QSettings::IniFormat);
if (!s.childGroups().contains("Script")) {
qWarning() << "Missing [Script] section in" << ini_file;
return ret;
}
s.beginGroup("Script");
// Find out what language it's in
QString language_name = s.value("language").toString();
LanguageEngine* engine = EngineForLanguage(language_name);
if (!engine) {
qWarning() << "Unknown language" << language_name << "in" << ini_file;
return ret;
}
ret.language_ = engine->language();
// Load the rest of the metadata
ret.path_ = path;
ret.name_ = s.value("name").toString();
ret.description_ = s.value("description").toString();
ret.author_ = s.value("author").toString();
ret.url_ = s.value("url").toString();
ret.script_file_ = path + "/" + s.value("script_file").toString();
// Load the script - TODO: move somewhere else
ret.loaded_ = engine->CreateScript(path, ret.script_file_);
if (ret.loaded_) {
ret.loaded_->Init();
}
return ret;
}
int ScriptManager::rowCount(const QModelIndex& parent) const {
if (parent.isValid())
return 0;
return info_.count();
}
QVariant ScriptManager::data(const QModelIndex& index, int role) const {
if (index.parent().isValid() || index.row() < 0 || index.row() >= info_.count())
return QVariant();
const ScriptInfo& info = info_[index.row()];
switch (role) {
case Qt::DisplayRole:
return info.name_;
case Role_Author:
return info.author_;
case Role_Description:
return info.description_;
case Role_Language:
return info.language_;
case Role_ScriptFile:
return info.script_file_;
case Role_Url:
return info.url_;
default:
return QVariant();
}
}

View File

@ -0,0 +1,87 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Clementine is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTMANAGER_H
#define SCRIPTMANAGER_H
#include <QAbstractItemModel>
#include <QStringList>
class LanguageEngine;
class Script;
class ScriptManager : public QAbstractListModel {
Q_OBJECT
public:
ScriptManager(QObject* parent = 0);
~ScriptManager();
enum Role {
Role_Description = Qt::UserRole,
Role_Author,
Role_Url,
Role_Language,
Role_ScriptFile,
RoleCount
};
enum Language {
Language_Unknown = 0,
Language_Python,
};
static const char* kIniFileName;
// QAbstractListModel
int rowCount(const QModelIndex& parent) const;
QVariant data(const QModelIndex& index, int role) const;
private:
struct ScriptInfo {
ScriptInfo() : language_(Language_Unknown), loaded_(NULL) {}
bool is_valid() const { return language_ != Language_Unknown; }
QString path_;
QString name_;
QString description_;
QString author_;
QString url_;
Language language_;
QString script_file_;
Script* loaded_;
};
void Reset();
ScriptInfo LoadScriptInfo(const QString& path);
LanguageEngine* EngineForLanguage(const QString& language_name) const;
LanguageEngine* EngineForLanguage(Language language) const;
private:
QList<LanguageEngine*> engines_;
QStringList search_paths_;
QList<ScriptInfo> info_;
};
#endif // SCRIPTMANAGER_H

View File

@ -1888,6 +1888,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1902,6 +1902,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1892,6 +1892,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1888,6 +1888,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1926,6 +1926,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1927,6 +1927,9 @@ msgstr ""
msgid "Score"
msgstr "Výsledek"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Se skladbami, které poslouchám, dělat to, čemu se říká \"scrobble\""

View File

@ -1888,6 +1888,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1893,6 +1893,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Scrobble-spor som jeg lytter til"

View File

@ -1930,6 +1930,9 @@ msgstr ""
msgid "Score"
msgstr "Bewertung"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Stücke die ich höre \"scrobbeln\""

View File

@ -1937,6 +1937,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Κάνε \"srobble\" τα κομμάτια που ακούω"

View File

@ -1877,6 +1877,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1893,6 +1893,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Scrobble tracks that I listen to"

View File

@ -1890,6 +1890,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Scrobble tracks that I listen to"

View File

@ -1888,6 +1888,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1938,6 +1938,9 @@ msgstr ""
msgid "Score"
msgstr "Puntuación"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Enviar las pistas que reproduzco"

View File

@ -1890,6 +1890,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1888,6 +1888,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1890,6 +1890,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1943,6 +1943,9 @@ msgstr ""
msgid "Score"
msgstr "Score"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Envoyer les titres des pistes que j'écoute (scrobble)"

View File

@ -1894,6 +1894,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1896,6 +1896,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1888,6 +1888,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1894,6 +1894,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1926,6 +1926,9 @@ msgstr ""
msgid "Score"
msgstr "Pontszám"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Az általam hallgatott számok Scrobble funkcióval történő figyelése"

View File

@ -1934,6 +1934,9 @@ msgstr ""
msgid "Score"
msgstr "Punteggio"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Scrobbling delle tracce ascoltate"

View File

@ -1916,6 +1916,9 @@ msgstr ""
msgid "Score"
msgstr "スコア"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "聴取するトラックを Scrobble する"

View File

@ -1890,6 +1890,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1888,6 +1888,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1902,6 +1902,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Fortell last.fm om sangene jeg har lyttet til"

View File

@ -1921,6 +1921,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Scrobble de tracks waar ik naar luister"

View File

@ -1888,6 +1888,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1921,6 +1921,9 @@ msgstr ""
msgid "Score"
msgstr "Wynik"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Wysyłaj informacje o utworach których słucham"

View File

@ -1931,6 +1931,9 @@ msgstr ""
msgid "Score"
msgstr "Pontuação"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Enviar as faixas que eu oiço"

View File

@ -1927,6 +1927,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Adicionar ao meu perfil os dados das músicas que eu ouvir"

View File

@ -1889,6 +1889,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1921,6 +1921,9 @@ msgstr ""
msgid "Score"
msgstr "Счет"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Скробблить треки, которые я слушаю"

View File

@ -1917,6 +1917,9 @@ msgstr ""
msgid "Score"
msgstr "Skóre"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Skrobblovať skladby, ktoré počúvam"

View File

@ -1920,6 +1920,9 @@ msgstr ""
msgid "Score"
msgstr "Rezultat"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Pošlji podatke o predvajanih skladbah"

View File

@ -1893,6 +1893,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1922,6 +1922,9 @@ msgstr ""
msgid "Score"
msgstr "Poäng"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Skrobbla låtar som jag lyssnar på"

View File

@ -1920,6 +1920,9 @@ msgstr ""
msgid "Score"
msgstr "Puan"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Dinlediğim parçaları skropla"

View File

@ -1878,6 +1878,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1922,6 +1922,9 @@ msgstr ""
msgid "Score"
msgstr "Рахунок"
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr "Скробблити доріжки, які я слухаю"

View File

@ -1892,6 +1892,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -1893,6 +1893,9 @@ msgstr ""
msgid "Score"
msgstr ""
msgid "Script Manager"
msgstr ""
msgid "Scrobble tracks that I listen to"
msgstr ""

View File

@ -96,6 +96,11 @@
# include "visualisations/visualisationcontainer.h"
#endif
#ifdef HAVE_SCRIPTING
# include "scripting/scriptdialog.h"
# include "scripting/scriptmanager.h"
#endif
#include <QCloseEvent>
#include <QDir>
#include <QFileDialog>
@ -166,6 +171,9 @@ MainWindow::MainWindow(QWidget* parent)
#endif
#ifdef ENABLE_WIIMOTEDEV
wiimotedev_shortcuts_(NULL),
#endif
#ifdef HAVE_SCRIPTING
scripts_(new ScriptManager(this)),
#endif
playlist_menu_(new QMenu(this)),
library_sort_model_(new QSortFilterProxyModel(this)),
@ -311,7 +319,7 @@ MainWindow::MainWindow(QWidget* parent)
#ifdef HAVE_LIBLASTFM
connect(ui_->action_cover_manager, SIGNAL(triggered()), SLOT(ShowCoverManager()));
#else
ui_->action_cover_manager->setVisible(false);
ui_->action_cover_manager->setEnabled(false);
#endif
connect(ui_->action_equalizer, SIGNAL(triggered()), equalizer_.get(), SLOT(show()));
connect(ui_->action_transcode, SIGNAL(triggered()), SLOT(ShowTranscodeDialog()));
@ -323,6 +331,12 @@ MainWindow::MainWindow(QWidget* parent)
background_streams_, SLOT(AllGloryToTheHypnotoad(bool)));
connect(ui_->action_queue_manager, SIGNAL(triggered()), SLOT(ShowQueueManager()));
#ifdef HAVE_SCRIPTING
connect(ui_->action_script_manager, SIGNAL(triggered()), SLOT(ShowScriptDialog()));
#else
ui_->action_script_manager->setEnabled(false);
#endif
// Give actions to buttons
ui_->forward_button->setDefaultAction(ui_->action_next_track);
ui_->back_button->setDefaultAction(ui_->action_previous_track);
@ -1781,3 +1795,13 @@ void MainWindow::PlaylistCurrentChanged(const QModelIndex& proxy_current) {
if (source_current != playlist_menu_index_)
playlist_menu_index_ = QModelIndex();
}
void MainWindow::ShowScriptDialog() {
#ifdef HAVE_SCRIPTING
if (!script_dialog_) {
script_dialog_.reset(new ScriptDialog);
script_dialog_->SetManager(scripts_);
}
script_dialog_->show();
#endif
}

View File

@ -57,6 +57,8 @@ class QueueManager;
class RadioItem;
class RadioModel;
class RadioViewContainer;
class ScriptDialog;
class ScriptManager;
class Song;
class SongInfoBase;
class SongInfoView;
@ -205,6 +207,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
void OpenSettingsDialog();
void OpenSettingsDialogAtPage(SettingsDialog::Page page);
void ShowSongInfoConfig();
void ShowScriptDialog();
void SaveGeometry();
@ -258,6 +261,7 @@ class MainWindow : public QMainWindow, public PlatformInterface {
boost::scoped_ptr<OrganiseDialog> organise_dialog_;
boost::scoped_ptr<QueueManager> queue_manager_;
#ifdef ENABLE_VISUALISATIONS
boost::scoped_ptr<VisualisationContainer> visualisation_;
#endif
@ -266,6 +270,11 @@ class MainWindow : public QMainWindow, public PlatformInterface {
boost::scoped_ptr<WiimotedevShortcuts> wiimotedev_shortcuts_;
#endif
#ifdef HAVE_SCRIPTING
ScriptManager* scripts_;
boost::scoped_ptr<ScriptDialog> script_dialog_;
#endif
QMenu* playlist_menu_;
QAction* playlist_play_pause_;
QAction* playlist_stop_after_;

View File

@ -36,7 +36,7 @@
<number>0</number>
</property>
<item>
<widget class="FancyTabWidget" name="tabs" />
<widget class="FancyTabWidget" name="tabs" native="true"/>
</item>
<item>
<widget class="NowPlayingWidget" name="now_playing" native="true"/>
@ -390,7 +390,7 @@
<x>0</x>
<y>0</y>
<width>1131</width>
<height>25</height>
<height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuMusic">
@ -443,7 +443,7 @@
</property>
<addaction name="action_rain"/>
<addaction name="action_hypnotoad"/>
<addaction name="action_kittens" />
<addaction name="action_kittens"/>
</widget>
<widget class="QMenu" name="menuTools">
<property name="title">
@ -451,6 +451,7 @@
</property>
<addaction name="action_cover_manager"/>
<addaction name="action_queue_manager"/>
<addaction name="action_script_manager"/>
<addaction name="action_equalizer"/>
<addaction name="action_visualisations"/>
<addaction name="action_transcode"/>
@ -750,6 +751,11 @@
<string>Ctrl+M</string>
</property>
</action>
<action name="action_script_manager">
<property name="text">
<string>Script Manager</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
@ -798,7 +804,6 @@
<class>FancyTabWidget</class>
<extends>QWidget</extends>
<header>widgets/fancytabwidget.h</header>
<container>0</container>
</customwidget>
</customwidgets>
<resources>