1
0
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:
David Sansome 2010-12-31 20:29:52 +00:00
parent 5b1000834f
commit dac0d071ea
16 changed files with 425 additions and 13 deletions

View File

@ -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
)

View File

@ -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)

View File

@ -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;
};

View File

@ -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

View 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 };
};

View File

@ -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();

View 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
};

View File

@ -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);
}

View 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_;
};

View File

@ -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

View File

@ -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_;
};

View 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

View 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;
};

View File

@ -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) {

View File

@ -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

View File

@ -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);