mirror of
https://github.com/clementine-player/Clementine
synced 2025-01-31 03:27:40 +01:00
Add bindings for Song, PlaylistItem, PlaylistItemPtr and EngineBase, add a "player" attribute to the clementine module
This commit is contained in:
parent
5b1000834f
commit
dac0d071ea
@ -40,7 +40,7 @@ macro(add_sip_bindings outputvar)
|
||||
# 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}"
|
||||
COMMAND "awk" "/^\\s*(class|namespace) +([A-Za-z0-9]+)/ {ORS=\";\"; print $2}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${included_file}"
|
||||
OUTPUT_VARIABLE classes
|
||||
)
|
||||
|
@ -712,6 +712,14 @@ if(HAVE_SCRIPTING)
|
||||
add_sip_bindings(SOURCES
|
||||
scripting/python/clementine.sip
|
||||
)
|
||||
|
||||
# The code that detects what files get generated by sip isn't perfect, so
|
||||
# add any extra ones here
|
||||
list(APPEND SOURCES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementinePlaylistItemOptions.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementinePlaylistItemPtr.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/sipclementineQList0100PlaylistItemPtr.cpp
|
||||
)
|
||||
endif(HAVE_SCRIPTING_PYTHON)
|
||||
endif(HAVE_SCRIPTING)
|
||||
|
||||
|
@ -29,6 +29,8 @@ public:
|
||||
virtual ScriptManager::Language language() const = 0;
|
||||
virtual QString name() const = 0;
|
||||
|
||||
virtual void Init(const ScriptManager::GlobalData& data) = 0;
|
||||
|
||||
virtual Script* CreateScript(const QString& path, const QString& script_file) = 0;
|
||||
};
|
||||
|
||||
|
@ -3,4 +3,7 @@
|
||||
%Import QtCore/QtCoremod.sip
|
||||
%Import QtGui/QtGuimod.sip
|
||||
|
||||
%Include engine_fwd.sip
|
||||
%Include player.sip
|
||||
%Include playlistitem.sip
|
||||
%Include song.sip
|
||||
|
9
src/scripting/python/engine_fwd.sip
Normal file
9
src/scripting/python/engine_fwd.sip
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Engine {
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "engines/engine_fwd.h"
|
||||
%End
|
||||
|
||||
enum State { Empty, Idle, Playing, Paused };
|
||||
enum TrackChangeType { First, Manual, Auto };
|
||||
};
|
@ -5,16 +5,16 @@ class Player : QObject {
|
||||
%End
|
||||
|
||||
public:
|
||||
// Engine::State GetState() const;
|
||||
Engine::State GetState() const;
|
||||
int GetVolume() const;
|
||||
|
||||
//PlaylistItemPtr GetCurrentItem() const { return current_item_; }
|
||||
//PlaylistItemPtr GetItemAt(int pos) const;
|
||||
PlaylistItemPtr GetCurrentItem() const;
|
||||
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);
|
||||
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
|
||||
@ -45,9 +45,9 @@ signals:
|
||||
void PlaylistFinished();
|
||||
void VolumeChanged(int volume);
|
||||
void Error(const QString& message);
|
||||
//void TrackSkipped(PlaylistItemPtr old_track);
|
||||
void TrackSkipped(PlaylistItemPtr old_track);
|
||||
|
||||
//void ForceShowOSD(Song);
|
||||
void ForceShowOSD(Song);
|
||||
|
||||
private:
|
||||
Player();
|
||||
|
97
src/scripting/python/playlistitem.sip
Normal file
97
src/scripting/python/playlistitem.sip
Normal file
@ -0,0 +1,97 @@
|
||||
%ModuleCode
|
||||
#include "playlist/playlistitem.h"
|
||||
#include "scripting/python/sharedpointermanager.h"
|
||||
|
||||
template<> SharedPointerManager<PlaylistItem>::pointer_map_type*
|
||||
SharedPointerManager<PlaylistItem>::_pointer_map(NULL);
|
||||
%End
|
||||
|
||||
|
||||
class PlaylistItem {
|
||||
%TypeHeaderCode
|
||||
#include "playlist/playlistitem.h"
|
||||
#include "scripting/python/sharedpointermanager.h"
|
||||
%End
|
||||
|
||||
public:
|
||||
static PlaylistItem* NewFromType(const QString& type);
|
||||
static PlaylistItem* NewFromSongsTable(const QString& table, const Song& song);
|
||||
|
||||
enum Option {
|
||||
Default,
|
||||
SpecialPlayBehaviour,
|
||||
ContainsMultipleTracks,
|
||||
PauseDisabled,
|
||||
LastFMControls,
|
||||
};
|
||||
typedef QFlags<PlaylistItem::Option> Options;
|
||||
|
||||
QString type() const;
|
||||
Options options() const;
|
||||
|
||||
void Reload();
|
||||
void BackgroundReload();
|
||||
|
||||
Song Metadata() const;
|
||||
QUrl Url() const;
|
||||
|
||||
void SetTemporaryMetadata(const Song& metadata);
|
||||
void ClearTemporaryMetadata();
|
||||
bool HasTemporaryMetadata() const;
|
||||
|
||||
void SetDynamicHistory(bool history);
|
||||
bool IsDynamicHistory() const;
|
||||
|
||||
bool IsLocalLibraryItem() const;
|
||||
|
||||
~PlaylistItem();
|
||||
%MethodCode
|
||||
// Don't actually destroy the PlaylistItem, just decrement the reference
|
||||
// count of Python's shared_ptr.
|
||||
SharedPointerManager<PlaylistItem>::SubRef(sipCpp);
|
||||
return;
|
||||
%End
|
||||
|
||||
private:
|
||||
PlaylistItem();
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<PlaylistItem> PlaylistItemPtr;
|
||||
typedef QList<PlaylistItemPtr> PlaylistItemList;
|
||||
|
||||
|
||||
%MappedType PlaylistItemPtr
|
||||
{
|
||||
%TypeHeaderCode
|
||||
#include "playlist/playlistitem.h"
|
||||
#include "scripting/python/sharedpointermanager.h"
|
||||
%End
|
||||
|
||||
%ConvertFromTypeCode
|
||||
if (!sipCpp)
|
||||
return NULL;
|
||||
|
||||
// Add an extra reference...
|
||||
SharedPointerManager<PlaylistItem>::AddRef(*sipCpp);
|
||||
|
||||
PyObject* o = sipConvertFromType(sipCpp->get(), sipType_PlaylistItem, Py_None);
|
||||
return o;
|
||||
%End
|
||||
|
||||
%ConvertToTypeCode
|
||||
if(sipIsErr == NULL)
|
||||
return PyInstance_Check(sipPy);
|
||||
|
||||
int iserr = 0;
|
||||
PlaylistItem* ord = reinterpret_cast<PlaylistItem*>(
|
||||
sipForceConvertToType(sipPy, sipType_PlaylistItem, Py_None, SIP_NO_CONVERTORS, NULL, &iserr));
|
||||
|
||||
if (iserr){
|
||||
*sipIsErr = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*sipCppPtr = SharedPointerManager<PlaylistItem>::CreatePointer(ord);
|
||||
return 1;
|
||||
%End
|
||||
};
|
@ -29,6 +29,10 @@ PythonEngine::PythonEngine()
|
||||
{
|
||||
}
|
||||
|
||||
void PythonEngine::Init(const ScriptManager::GlobalData& data) {
|
||||
data_ = data;
|
||||
}
|
||||
|
||||
Script* PythonEngine::CreateScript(const QString& path, const QString& script_file) {
|
||||
// Initialise Python if it hasn't been done yet
|
||||
if (!initialised_) {
|
||||
@ -43,5 +47,5 @@ Script* PythonEngine::CreateScript(const QString& path, const QString& script_fi
|
||||
initialised_ = true;
|
||||
}
|
||||
|
||||
return new PythonScript(path, script_file);
|
||||
return new PythonScript(this, path, script_file);
|
||||
}
|
||||
|
@ -27,9 +27,14 @@ public:
|
||||
ScriptManager::Language language() const { return ScriptManager::Language_Python; }
|
||||
QString name() const { return "python"; }
|
||||
|
||||
const ScriptManager::GlobalData& data() const { return data_; }
|
||||
|
||||
void Init(const ScriptManager::GlobalData& data);
|
||||
Script* CreateScript(const QString& path, const QString& script_file);
|
||||
|
||||
private:
|
||||
ScriptManager::GlobalData data_;
|
||||
|
||||
bool initialised_;
|
||||
};
|
||||
|
||||
|
@ -16,15 +16,53 @@
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
#include <sip.h>
|
||||
|
||||
#include "pythonengine.h"
|
||||
#include "pythonscript.h"
|
||||
#include "sipAPIclementine.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QtDebug>
|
||||
|
||||
|
||||
PythonScript::PythonScript(const QString& path, const QString& script_file)
|
||||
static const sipAPIDef* GetSIPApi() {
|
||||
#if defined(SIP_USE_PYCAPSULE)
|
||||
return (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0);
|
||||
#else
|
||||
PyObject *sip_module;
|
||||
PyObject *sip_module_dict;
|
||||
PyObject *c_api;
|
||||
|
||||
/* Import the SIP module. */
|
||||
sip_module = PyImport_ImportModule("sip");
|
||||
|
||||
if (sip_module == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Get the module's dictionary. */
|
||||
sip_module_dict = PyModule_GetDict(sip_module);
|
||||
|
||||
/* Get the "_C_API" attribute. */
|
||||
c_api = PyDict_GetItemString(sip_module_dict, "_C_API");
|
||||
|
||||
if (c_api == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Sanity check that it is the right type. */
|
||||
if (!PyCObject_Check(c_api))
|
||||
return NULL;
|
||||
|
||||
/* Get the actual pointer from the object. */
|
||||
return (const sipAPIDef *)PyCObject_AsVoidPtr(c_api);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
PythonScript::PythonScript(PythonEngine* engine,
|
||||
const QString& path, const QString& script_file)
|
||||
: Script(path, script_file),
|
||||
engine_(engine),
|
||||
interpreter_(NULL)
|
||||
{
|
||||
}
|
||||
@ -40,6 +78,15 @@ bool PythonScript::Init() {
|
||||
// Create a python interpreter
|
||||
PyEval_AcquireLock();
|
||||
interpreter_ = Py_NewInterpreter();
|
||||
|
||||
// Get the clementine module so we can put stuff in it
|
||||
PyObject* clementine = PyImport_ImportModule("clementine");
|
||||
|
||||
const sipAPIDef* sip_api = GetSIPApi();
|
||||
PyObject* player = sip_api->api_convert_from_type(
|
||||
engine_->data().player_, sipType_Player, NULL);
|
||||
PyModule_AddObject(clementine, "player", player);
|
||||
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
// Get a file stream from the file handle
|
||||
|
@ -22,14 +22,19 @@
|
||||
|
||||
struct _ts; // PyThreadState
|
||||
|
||||
class PythonEngine;
|
||||
|
||||
class PythonScript : public Script {
|
||||
public:
|
||||
PythonScript(const QString& path, const QString& script_file);
|
||||
PythonScript(PythonEngine* engine,
|
||||
const QString& path, const QString& script_file);
|
||||
|
||||
bool Init();
|
||||
bool Unload();
|
||||
|
||||
private:
|
||||
PythonEngine* engine_;
|
||||
|
||||
_ts* interpreter_;
|
||||
};
|
||||
|
||||
|
78
src/scripting/python/sharedpointermanager.h
Normal file
78
src/scripting/python/sharedpointermanager.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* 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 SHAREDPOINTERMANAGER_H
|
||||
#define SHAREDPOINTERMANAGER_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
template<typename T>
|
||||
struct SharedPointerManager {
|
||||
typedef T cpp_type;
|
||||
typedef boost::shared_ptr<cpp_type> smart_pointer_type;
|
||||
typedef std::pair<smart_pointer_type, int> smart_pointer_count;
|
||||
typedef std::map<cpp_type*, smart_pointer_count> pointer_map_type;
|
||||
|
||||
static pointer_map_type* _pointer_map;
|
||||
|
||||
static void init() {
|
||||
if(!_pointer_map)
|
||||
_pointer_map = new pointer_map_type();
|
||||
}
|
||||
|
||||
// Add a new reference or increment a previous reference
|
||||
static int AddRef(smart_pointer_type& ref) {
|
||||
init();
|
||||
|
||||
typename pointer_map_type::iterator it = _pointer_map->find(ref.get());
|
||||
if (it != _pointer_map->end())
|
||||
return ++(*it).second.second;
|
||||
|
||||
_pointer_map->insert(std::make_pair(ref.get(), std::make_pair(ref, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Release a reference
|
||||
static int SubRef(cpp_type* ptr) {
|
||||
init();
|
||||
|
||||
typename pointer_map_type::iterator it = _pointer_map->find(ptr);
|
||||
if (it == _pointer_map->end())
|
||||
return -1;
|
||||
|
||||
int count = --(*it).second.second;
|
||||
if (0 == count)
|
||||
_pointer_map->erase(it);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Create a new smart pointer instance (from copy in map if available)
|
||||
static smart_pointer_type* CreatePointer(cpp_type* ptr) {
|
||||
init();
|
||||
|
||||
typename pointer_map_type::iterator it = _pointer_map->find(ptr);
|
||||
if (it == _pointer_map->end())
|
||||
return new smart_pointer_type(ptr);
|
||||
else
|
||||
return new smart_pointer_type((*it).second.first);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // SHAREDPOINTERMANAGER_H
|
134
src/scripting/python/song.sip
Normal file
134
src/scripting/python/song.sip
Normal file
@ -0,0 +1,134 @@
|
||||
class Song {
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "core/song.h"
|
||||
%End
|
||||
|
||||
public:
|
||||
Song();
|
||||
Song(const Song& other);
|
||||
|
||||
enum FileType {
|
||||
Type_Unknown,
|
||||
Type_Asf,
|
||||
Type_Flac,
|
||||
Type_Mp4,
|
||||
Type_Mpc,
|
||||
Type_Mpeg,
|
||||
Type_OggFlac,
|
||||
Type_OggSpeex,
|
||||
Type_OggVorbis,
|
||||
Type_Aiff,
|
||||
Type_Wav,
|
||||
Type_TrueAudio,
|
||||
|
||||
Type_Stream,
|
||||
};
|
||||
|
||||
static QString TextForFiletype(FileType type);
|
||||
QString TextForFiletype() const;
|
||||
|
||||
// Constructors
|
||||
void Init(const QString& title, const QString& artist, const QString& album, int length);
|
||||
void Init(const QString& title, const QString& artist, const QString& album, int beginning, int end);
|
||||
void InitFromFile(const QString& filename, int directory_id);
|
||||
|
||||
// Simple accessors
|
||||
bool is_valid() const;
|
||||
int id() const;
|
||||
|
||||
QString title() const;
|
||||
QString album() const;
|
||||
QString artist() const;
|
||||
QString albumartist() const;
|
||||
QString composer() const;
|
||||
int track() const;
|
||||
int disc() const;
|
||||
float bpm() const;
|
||||
int year() const;
|
||||
const QString& genre() const;
|
||||
const QString& comment() const;
|
||||
bool is_compilation() const;
|
||||
float rating() const;
|
||||
int playcount() const;
|
||||
int skipcount() const;
|
||||
int lastplayed() const;
|
||||
int score() const;
|
||||
|
||||
int beginning() const;
|
||||
int end() const;
|
||||
|
||||
int length() const;
|
||||
|
||||
int bitrate() const;
|
||||
int samplerate() const;
|
||||
|
||||
int directory_id() const;
|
||||
const QString& filename() const;
|
||||
const QString& basefilename() const;
|
||||
uint mtime() const;
|
||||
uint ctime() const;
|
||||
int filesize() const;
|
||||
FileType filetype() const;
|
||||
|
||||
QString art_automatic() const;
|
||||
QString art_manual() const;
|
||||
|
||||
QImage image() const;
|
||||
|
||||
// Pretty accessors
|
||||
QString PrettyTitle() const;
|
||||
QString PrettyTitleWithArtist() const;
|
||||
QString PrettyLength() const;
|
||||
QString PrettyYear() const;
|
||||
|
||||
QString TitleWithCompilationArtist() const;
|
||||
|
||||
// Setters
|
||||
bool IsEditable() const;
|
||||
bool Save() const;
|
||||
void BackgroundSave() const;
|
||||
|
||||
void set_id(int id);
|
||||
void set_valid(bool v);
|
||||
void set_title(const QString& v);
|
||||
|
||||
void set_album(const QString& v);
|
||||
void set_artist(const QString& v);
|
||||
void set_albumartist(const QString& v);
|
||||
void set_composer(const QString& v);
|
||||
void set_track(int v);
|
||||
void set_disc(int v);
|
||||
void set_bpm(float v);
|
||||
void set_year(int v);
|
||||
void set_genre(const QString& v);
|
||||
void set_genre(int id);
|
||||
void set_comment(const QString& v);
|
||||
void set_compilation(bool v);
|
||||
void set_sampler(bool v);
|
||||
void set_beginning(int v);
|
||||
void set_end(int v);
|
||||
void set_length(int v);
|
||||
void set_bitrate(int v);
|
||||
void set_samplerate(int v);
|
||||
void set_mtime(int v);
|
||||
void set_ctime(int v);
|
||||
void set_filesize(int v);
|
||||
void set_filetype(FileType v);
|
||||
void set_art_automatic(const QString& v);
|
||||
void set_art_manual(const QString& v);
|
||||
void set_image(const QImage& i);
|
||||
void set_forced_compilation_on(bool v);
|
||||
void set_forced_compilation_off(bool v);
|
||||
void set_rating(float v);
|
||||
void set_playcount(int v);
|
||||
void set_skipcount(int v);
|
||||
void set_lastplayed(int v);
|
||||
void set_score(int v);
|
||||
void set_filename(const QString& v);
|
||||
void set_basefilename(const QString& v);
|
||||
void set_directory_id(int v);
|
||||
|
||||
// Comparison functions
|
||||
bool IsMetadataEqual(const Song& other) const;
|
||||
};
|
@ -32,21 +32,28 @@
|
||||
const char* ScriptManager::kIniFileName = "script.ini";
|
||||
|
||||
ScriptManager::ScriptManager(QObject* parent)
|
||||
: QAbstractListModel(parent)
|
||||
: QAbstractListModel(parent),
|
||||
player_(NULL)
|
||||
{
|
||||
#ifdef HAVE_SCRIPTING_PYTHON
|
||||
engines_ << new PythonEngine;
|
||||
#endif
|
||||
|
||||
search_paths_ << Utilities::GetConfigPath(Utilities::Path_Scripts);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
ScriptManager::~ScriptManager() {
|
||||
qDeleteAll(engines_);
|
||||
}
|
||||
|
||||
void ScriptManager::Init(const GlobalData& data) {
|
||||
foreach (LanguageEngine* engine, engines_) {
|
||||
engine->Init(data);
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
void ScriptManager::Reset() {
|
||||
// Remove any scripts that aren't loaded
|
||||
for (int i=0 ; i<info_.count() ; ++i) {
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <QStringList>
|
||||
|
||||
class LanguageEngine;
|
||||
class Player;
|
||||
class Script;
|
||||
|
||||
class ScriptManager : public QAbstractListModel {
|
||||
@ -46,8 +47,17 @@ public:
|
||||
Language_Python,
|
||||
};
|
||||
|
||||
struct GlobalData {
|
||||
GlobalData() : player_(NULL) {}
|
||||
GlobalData(Player* player) : player_(player) {}
|
||||
|
||||
Player* player_;
|
||||
};
|
||||
|
||||
static const char* kIniFileName;
|
||||
|
||||
void Init(const GlobalData& data);
|
||||
|
||||
// QAbstractListModel
|
||||
int rowCount(const QModelIndex& parent) const;
|
||||
QVariant data(const QModelIndex& index, int role) const;
|
||||
@ -82,6 +92,8 @@ private:
|
||||
|
||||
QStringList search_paths_;
|
||||
QList<ScriptInfo> info_;
|
||||
|
||||
Player* player_;
|
||||
};
|
||||
|
||||
#endif // SCRIPTMANAGER_H
|
||||
|
@ -332,6 +332,7 @@ MainWindow::MainWindow(QWidget* parent)
|
||||
connect(ui_->action_queue_manager, SIGNAL(triggered()), SLOT(ShowQueueManager()));
|
||||
|
||||
#ifdef HAVE_SCRIPTING
|
||||
scripts_->Init(ScriptManager::GlobalData(player_));
|
||||
connect(ui_->action_script_manager, SIGNAL(triggered()), SLOT(ShowScriptDialog()));
|
||||
#else
|
||||
ui_->action_script_manager->setEnabled(false);
|
||||
|
Loading…
x
Reference in New Issue
Block a user