diff options
author | Remko Tronçon <git@el-tramo.be> | 2014-01-19 11:46:51 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2014-01-19 16:49:19 (GMT) |
commit | cbd01a5368f0b761d2032d75c9f7dfde2bf61578 (patch) | |
tree | 5016505b1e977e84655cc3bba4435ef7cb80e811 /Sluift | |
parent | 4083d6da47ac0e3b77da9c7c222a9439b3e1c04c (diff) | |
download | swift-contrib-cbd01a5368f0b761d2032d75c9f7dfde2bf61578.zip swift-contrib-cbd01a5368f0b761d2032d75c9f7dfde2bf61578.tar.bz2 |
Sluift: Add iTunes & PEP User Tune support
Change-Id: I25b3840bb40ce38531922cc737bc82828e026d3f
Diffstat (limited to 'Sluift')
-rw-r--r-- | Sluift/.gitignore | 1 | ||||
-rw-r--r-- | Sluift/ElementConvertors/ElementConvertors.ipp | 4 | ||||
-rw-r--r-- | Sluift/ElementConvertors/SConscript | 1 | ||||
-rw-r--r-- | Sluift/ElementConvertors/UserTuneConvertor.cpp | 111 | ||||
-rw-r--r-- | Sluift/ElementConvertors/UserTuneConvertor.h | 29 | ||||
-rw-r--r-- | Sluift/Examples/Tunes.lua | 68 | ||||
-rw-r--r-- | Sluift/ITunesInterface.h | 38 | ||||
-rw-r--r-- | Sluift/ITunesInterface.mm | 58 | ||||
-rw-r--r-- | Sluift/Lua/LuaUtils.cpp | 11 | ||||
-rw-r--r-- | Sluift/Lua/LuaUtils.h | 1 | ||||
-rw-r--r-- | Sluift/SConscript | 11 | ||||
-rw-r--r-- | Sluift/SluiftGlobals.h | 6 | ||||
-rw-r--r-- | Sluift/core.lua | 18 | ||||
-rw-r--r-- | Sluift/sluift.cpp | 39 |
14 files changed, 392 insertions, 4 deletions
diff --git a/Sluift/.gitignore b/Sluift/.gitignore index e2d8bbf..4e4c2a4 100644 --- a/Sluift/.gitignore +++ b/Sluift/.gitignore @@ -4,5 +4,6 @@ sluift_dll.cpp sluift dll.c core.c +iTunes.h dll/ exe/ diff --git a/Sluift/ElementConvertors/ElementConvertors.ipp b/Sluift/ElementConvertors/ElementConvertors.ipp index b7b9166..da25eb6 100644 --- a/Sluift/ElementConvertors/ElementConvertors.ipp +++ b/Sluift/ElementConvertors/ElementConvertors.ipp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Remko Tronçon + * Copyright (c) 2013-2014 Remko Tronçon * Licensed under the GNU General Public License. * See the COPYING file for more information. */ @@ -10,6 +10,7 @@ #include <Sluift/ElementConvertors/PubSubItemsConvertor.h> #include <Sluift/ElementConvertors/PubSubOwnerRedirectConvertor.h> #include <Sluift/ElementConvertors/PubSubEventRedirectConvertor.h> +#include <Sluift/ElementConvertors/UserTuneConvertor.h> #include <Sluift/ElementConvertors/PubSubConfigureConvertor.h> #include <Sluift/ElementConvertors/PubSubEventDisassociateConvertor.h> #include <Sluift/ElementConvertors/PubSubOwnerAffiliationsConvertor.h> @@ -48,6 +49,7 @@ void LuaElementConvertors::registerConvertors() { convertors.push_back(boost::make_shared<PubSubItemsConvertor>(this)); convertors.push_back(boost::make_shared<PubSubOwnerRedirectConvertor>(this)); convertors.push_back(boost::make_shared<PubSubEventRedirectConvertor>(this)); + convertors.push_back(boost::make_shared<UserTuneConvertor>(this)); convertors.push_back(boost::make_shared<PubSubConfigureConvertor>(this)); convertors.push_back(boost::make_shared<PubSubEventDisassociateConvertor>(this)); convertors.push_back(boost::make_shared<PubSubOwnerAffiliationsConvertor>(this)); diff --git a/Sluift/ElementConvertors/SConscript b/Sluift/ElementConvertors/SConscript index e98f7c4..921e325 100644 --- a/Sluift/ElementConvertors/SConscript +++ b/Sluift/ElementConvertors/SConscript @@ -8,6 +8,7 @@ convertors = [ env.File("PubSubItemsConvertor.cpp"), env.File("PubSubOwnerRedirectConvertor.cpp"), env.File("PubSubEventRedirectConvertor.cpp"), + env.File("UserTuneConvertor.cpp"), env.File("PubSubConfigureConvertor.cpp"), env.File("PubSubEventDisassociateConvertor.cpp"), env.File("PubSubOwnerAffiliationsConvertor.cpp"), diff --git a/Sluift/ElementConvertors/UserTuneConvertor.cpp b/Sluift/ElementConvertors/UserTuneConvertor.cpp new file mode 100644 index 0000000..22ca94e --- /dev/null +++ b/Sluift/ElementConvertors/UserTuneConvertor.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ElementConvertors/UserTuneConvertor.h> + +#include <lua.hpp> +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> + + + +#pragma clang diagnostic ignored "-Wunused-private-field" + +using namespace Swift; + +UserTuneConvertor::UserTuneConvertor(LuaElementConvertors* convertors) : + GenericLuaElementConvertor<UserTune>("user_tune"), + convertors(convertors) { +} + +UserTuneConvertor::~UserTuneConvertor() { +} + +boost::shared_ptr<UserTune> UserTuneConvertor::doConvertFromLua(lua_State* L) { + boost::shared_ptr<UserTune> result = boost::make_shared<UserTune>(); + lua_getfield(L, -1, "rating"); + if (lua_isnumber(L, -1)) { + result->setRating(boost::numeric_cast<unsigned int>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "title"); + if (lua_isstring(L, -1)) { + result->setTitle(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "track"); + if (lua_isstring(L, -1)) { + result->setTrack(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "artist"); + if (lua_isstring(L, -1)) { + result->setArtist(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "uri"); + if (lua_isstring(L, -1)) { + result->setURI(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "source"); + if (lua_isstring(L, -1)) { + result->setSource(std::string(lua_tostring(L, -1))); + } + lua_pop(L, 1); + lua_getfield(L, -1, "length"); + if (lua_isnumber(L, -1)) { + result->setLength(boost::numeric_cast<unsigned int>(lua_tonumber(L, -1))); + } + lua_pop(L, 1); + return result; +} + +void UserTuneConvertor::doConvertToLua(lua_State* L, boost::shared_ptr<UserTune> payload) { + lua_createtable(L, 0, 0); + if (payload->getRating()) { + lua_pushnumber(L, (*payload->getRating())); + lua_setfield(L, -2, "rating"); + } + if (payload->getTitle()) { + lua_pushstring(L, (*payload->getTitle()).c_str()); + lua_setfield(L, -2, "title"); + } + if (payload->getTrack()) { + lua_pushstring(L, (*payload->getTrack()).c_str()); + lua_setfield(L, -2, "track"); + } + if (payload->getArtist()) { + lua_pushstring(L, (*payload->getArtist()).c_str()); + lua_setfield(L, -2, "artist"); + } + if (payload->getURI()) { + lua_pushstring(L, (*payload->getURI()).c_str()); + lua_setfield(L, -2, "uri"); + } + if (payload->getSource()) { + lua_pushstring(L, (*payload->getSource()).c_str()); + lua_setfield(L, -2, "source"); + } + if (payload->getLength()) { + lua_pushnumber(L, (*payload->getLength())); + lua_setfield(L, -2, "length"); + } +} + +boost::optional<LuaElementConvertor::Documentation> UserTuneConvertor::getDocumentation() const { + return Documentation( + "UserTune", + "This table has the following fields:\n\n" + "- `rating`: number (Optional)\n" + "- `title`: string (Optional)\n" + "- `track`: string (Optional)\n" + "- `artist`: string (Optional)\n" + "- `uri`: string (Optional)\n" + "- `source`: string (Optional)\n" + "- `length`: number (Optional)\n" + ); +} diff --git a/Sluift/ElementConvertors/UserTuneConvertor.h b/Sluift/ElementConvertors/UserTuneConvertor.h new file mode 100644 index 0000000..6f95164 --- /dev/null +++ b/Sluift/ElementConvertors/UserTuneConvertor.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Base/Override.h> + +#include <Sluift/GenericLuaElementConvertor.h> +#include <Swiften/Elements/UserTune.h> + +namespace Swift { + class LuaElementConvertors; + + class UserTuneConvertor : public GenericLuaElementConvertor<UserTune> { + public: + UserTuneConvertor(LuaElementConvertors* convertors); + virtual ~UserTuneConvertor(); + + virtual boost::shared_ptr<UserTune> doConvertFromLua(lua_State*) SWIFTEN_OVERRIDE; + virtual void doConvertToLua(lua_State*, boost::shared_ptr<UserTune>) SWIFTEN_OVERRIDE; + virtual boost::optional<Documentation> getDocumentation() const SWIFTEN_OVERRIDE; + + private: + LuaElementConvertors* convertors; + }; +} diff --git a/Sluift/Examples/Tunes.lua b/Sluift/Examples/Tunes.lua new file mode 100644 index 0000000..37ad996 --- /dev/null +++ b/Sluift/Examples/Tunes.lua @@ -0,0 +1,68 @@ +--[[ + Copyright (c) 2014 Remko Tronçon + Licensed under the GNU General Public License v3. + See Documentation/Licenses/GPLv3.txt for more information. +--]] + +--[[ + + Tune Publisher/Listener + + Publishes the currently playing tune in iTunes, and prints + the playing tunes of contacts + + The following environment variables are used: + - SLUIFT_JID, SWIFT_PASS: JID and password to log in with + - SLUIFT_DEBUG: Sets whether debugging should be turned on + +--]] + +require 'sluift' + +sluift.debug = os.getenv('SLUIFT_DEBUG') or false + +client = sluift.new_client(os.getenv('SLUIFT_JID'), os.getenv('SLUIFT_PASS')) +client:connect(function (c) + -- Send initial presence (with service discovery information) + c:set_caps_node('http://swift.im/Tunes') + c:set_disco_info{ + identities = {{name = 'Tunes'}}, + features = { + sluift.disco.features.DISCO_INFO, + sluift.disco.features.USER_TUNE .. '+notify' + }} + c:send_presence{priority = -1} + + local pubsub = c:pubsub(sluift.jid.to_bare(c:jid())) + local tunes_node = pubsub:node(sluift.disco.features.USER_TUNE) + local current_track = nil + while true do + -- Publish currently playing tune + if sluift.itunes then + local track = sluift.itunes.get_current_track() + if track ~= current_track then + tunes_node:publish{item = { + _type = 'user_tune', + title = track.name, + artist = track.artist, + track = track.track_number, + source = track.album, + length = track.length, + rating = track.rating and track.rating / 10 or nil + }} + current_track = track + end + end + + -- Print incoming events for a while + for event in c:pubsub_events{timeout = 1000} do + if event._type == 'pubsub_event_items' + and event.node == sluift.disco.features.USER_TUNE + and event.item.title then + print(event.from .. ' is playing "' + .. (event.item.artist or '<Unknown Artist>') .. ' - ' + .. event.item.title .. '"') + end + end + end +end) diff --git a/Sluift/ITunesInterface.h b/Sluift/ITunesInterface.h new file mode 100644 index 0000000..c3cccd2 --- /dev/null +++ b/Sluift/ITunesInterface.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#pragma once + +#include <Swiften/Base/API.h> + +#include <boost/shared_ptr.hpp> +#include <boost/optional/optional_fwd.hpp> + +namespace Swift { + class SWIFTEN_API ITunesInterface { + public: + struct Track { + std::string name; + std::string artist; + std::string album; + long trackNumber; + double duration; + long rating; + }; + + ITunesInterface(); + virtual ~ITunesInterface(); + + boost::optional<Track> getCurrentTrack() const; + + private: + bool haveApplication() const; + + private: + struct Private; + boost::shared_ptr<Private> p; + }; +} diff --git a/Sluift/ITunesInterface.mm b/Sluift/ITunesInterface.mm new file mode 100644 index 0000000..19c6253 --- /dev/null +++ b/Sluift/ITunesInterface.mm @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + +#include <Sluift/ITunesInterface.h> + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfour-char-constants" +#import <Sluift/iTunes.h> +#pragma clang diagnostic pop +#include <ScriptingBridge/ScriptingBridge.h> + +#include <boost/smart_ptr/make_shared.hpp> +#include <boost/optional.hpp> +#include <SwifTools/Cocoa/CocoaUtil.h> + +using namespace Swift; + +struct ITunesInterface::Private { + Private() : iTunes(nil) { + } + + iTunesApplication* iTunes; +}; + +ITunesInterface::ITunesInterface() : p(boost::make_shared<Private>()) { +} + +ITunesInterface::~ITunesInterface() { +} + +boost::optional<ITunesInterface::Track> ITunesInterface::getCurrentTrack() const { + if (!haveApplication()) { + return boost::optional<ITunesInterface::Track>(); + } + iTunesTrack* currentTrack = p->iTunes.currentTrack; + if (!currentTrack) { + return boost::optional<ITunesInterface::Track>(); + } + ITunesInterface::Track result; + result.name = NS2STDSTRING(currentTrack.name); + result.artist = NS2STDSTRING(currentTrack.artist); + result.album = NS2STDSTRING(currentTrack.album); + result.trackNumber = currentTrack.trackNumber; + result.duration = currentTrack.duration; + result.rating = currentTrack.rating; + return result; +} + + +bool ITunesInterface::haveApplication() const { + if (!p->iTunes) { + p->iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"]; + } + return p->iTunes != nil && [p->iTunes isRunning]; +} diff --git a/Sluift/Lua/LuaUtils.cpp b/Sluift/Lua/LuaUtils.cpp index 2192689..915f3cc 100644 --- a/Sluift/Lua/LuaUtils.cpp +++ b/Sluift/Lua/LuaUtils.cpp @@ -32,6 +32,17 @@ void Swift::Lua::registerTableToString(lua_State* L, int index) { lua_pop(L, 1); } +void Swift::Lua::registerTableEquals(lua_State* L, int index) { + index = Lua::absoluteOffset(L, index); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "register_table_equals"); + lua_pushvalue(L, index); + if (lua_pcall(L, 1, 0, 0) != 0) { + throw Lua::Exception(lua_tostring(L, -1)); + } + lua_pop(L, 1); +} + void Swift::Lua::registerGetByTypeIndex(lua_State* L, int index) { index = Lua::absoluteOffset(L, index); lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); diff --git a/Sluift/Lua/LuaUtils.h b/Sluift/Lua/LuaUtils.h index 19ab74e..105f249 100644 --- a/Sluift/Lua/LuaUtils.h +++ b/Sluift/Lua/LuaUtils.h @@ -23,6 +23,7 @@ namespace Swift { int convertTableToString(lua_State* L); void registerTableToString(lua_State* L, int index); + void registerTableEquals(lua_State* L, int index); void registerGetByTypeIndex(lua_State* L, int index); void registerHelp(lua_State* L, int index, const std::string& description, const std::string& parameters, const std::string& options); diff --git a/Sluift/SConscript b/Sluift/SConscript index c2bbff5..3cc1f29 100644 --- a/Sluift/SConscript +++ b/Sluift/SConscript @@ -1,6 +1,6 @@ import Version, os.path -Import(["env", "conf_env"]) +Import(["env"]) if env["SCONS_STAGE"] == "build" and not GetOption("help") and not env.get("HAVE_LUA", 0) : print "Warning: Lua was not found. Sluift will not be built." @@ -42,6 +42,8 @@ elif env["SCONS_STAGE"] == "build" : ] sluift_sources += env.SConscript("ElementConvertors/SConscript") + + sluift_env = env.Clone() sluift_env.UseFlags(env.get("LUA_FLAGS", {})) sluift_env.UseFlags(env["SWIFTEN_FLAGS"]) @@ -51,6 +53,13 @@ elif env["SCONS_STAGE"] == "build" : if sluift_env["PLATFORM"] == "win32" : sluift_env.Append(CPPDEFINES = ["SLUIFT_BUILD_DLL"]) + if sluift_env["PLATFORM"] == "darwin" and os.path.isdir("/Applications/iTunes.app") : + sluift_env.Append(FRAMEWORKS = ["ScriptingBridge"]) + sluift_env.Command("iTunes.h", "/Applications/iTunes.app", + "sdef ${SOURCE} | sdp -fh --basename iTunes -o ${TARGET.dir}") + sluift_env.Append(CPPDEFINES = ["HAVE_ITUNES"]) + sluift_sources += ["ITunesInterface.mm"] + # Generate Version.h version_header = "#pragma once\n\n" version_header += "#define SLUIFT_VERSION_STRING \"" + Version.getBuildVersion(env.Dir("#").abspath, "sluift") + "\"\n" diff --git a/Sluift/SluiftGlobals.h b/Sluift/SluiftGlobals.h index 5de7cfe..e89f495 100644 --- a/Sluift/SluiftGlobals.h +++ b/Sluift/SluiftGlobals.h @@ -9,6 +9,9 @@ #include <Sluift/LuaElementConvertors.h> #include <Swiften/EventLoop/SimpleEventLoop.h> #include <Swiften/Network/BoostNetworkFactories.h> +#ifdef HAVE_ITUNES +#include <Sluift/ITunesInterface.h> +#endif #include <signal.h> namespace Swift { @@ -25,5 +28,8 @@ namespace Swift { int coreLibIndex; int moduleLibIndex; sig_atomic_t interruptRequested; +#ifdef HAVE_ITUNES + ITunesInterface iTunes; +#endif }; } diff --git a/Sluift/core.lua b/Sluift/core.lua index f387354..1969690 100644 --- a/Sluift/core.lua +++ b/Sluift/core.lua @@ -72,7 +72,22 @@ local function register_table_tostring(table, print_functions) metatable.__tostring = table_tostring end end - return table +end + +-- FIXME: Not really a good or efficiant equals, but does the trick for now +local function table_equals(t1, t2) + return tostring(t1) == tostring(t2) +end + +local function register_table_equals(table) + if type(table) == 'table' then + local metatable = getmetatable(table) + if not metatable then + metatable = {} + setmetatable(table, metatable) + end + metatable.__eq = table_equals + end end local function merge_tables(...) @@ -983,6 +998,7 @@ return { register_help = register_help, register_class_help = register_class_help, register_table_tostring = register_table_tostring, + register_table_equals = register_table_equals, register_get_by_type_index = register_get_by_type_index, process_pubsub_event = process_pubsub_event, tprint = tprint, diff --git a/Sluift/sluift.cpp b/Sluift/sluift.cpp index 08ffd92..17990e8 100644 --- a/Sluift/sluift.cpp +++ b/Sluift/sluift.cpp @@ -33,6 +33,7 @@ #include <Swiften/IDN/IDNConverter.h> #include <Swiften/Crypto/CryptoProvider.h> #include <Swiften/Crypto/PlatformCryptoProvider.h> +#include <Sluift/ITunesInterface.h> using namespace Swift; @@ -176,7 +177,6 @@ SLUIFT_LUA_FUNCTION_WITH_HELP( return 1; } - /******************************************************************************* * JID Functions ******************************************************************************/ @@ -271,6 +271,34 @@ SLUIFT_LUA_FUNCTION(IDN, stringprep) { return 1; } +/******************************************************************************* + * iTunes Functions + ******************************************************************************/ + +#ifdef HAVE_ITUNES +SLUIFT_LUA_FUNCTION(iTunes, get_current_track) { + boost::optional<ITunesInterface::Track> track = Sluift::globals.iTunes.getCurrentTrack(); + if (!track) { + return 0; + } + lua_createtable(L, 0, 0); + lua_pushstring(L, track->artist.c_str()); + lua_setfield(L, -2, "artist"); + lua_pushstring(L, track->name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, track->album.c_str()); + lua_setfield(L, -2, "album"); + lua_pushinteger(L, track->trackNumber); + lua_setfield(L, -2, "track_number"); + lua_pushnumber(L, track->duration); + lua_setfield(L, -2, "duration"); + lua_pushinteger(L, track->rating); + lua_setfield(L, -2, "rating"); + Lua::registerTableToString(L, -1); + Lua::registerTableEquals(L, -1); + return 1; +} +#endif /******************************************************************************* * Module registration @@ -296,6 +324,11 @@ SLUIFT_API int luaopen_sluift(lua_State* L) { lua_call(L, 0, 1); Sluift::globals.coreLibIndex = luaL_ref(L, LUA_REGISTRYINDEX); + lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); + lua_getfield(L, -1, "Client"); + lua_setmetatable(L, -3); + lua_pop(L, 1); + // Register functions Lua::FunctionRegistry::getInstance().addFunctionsToTable(L, "Sluift"); Lua::FunctionRegistry::getInstance().createFunctionTable(L, "JID"); @@ -304,6 +337,10 @@ SLUIFT_API int luaopen_sluift(lua_State* L) { lua_setfield(L, -2, "base64"); Lua::FunctionRegistry::getInstance().createFunctionTable(L, "IDN"); lua_setfield(L, -2, "idn"); +#ifdef HAVE_ITUNES + Lua::FunctionRegistry::getInstance().createFunctionTable(L, "iTunes"); + lua_setfield(L, -2, "itunes"); +#endif // Register convenience functions lua_rawgeti(L, LUA_REGISTRYINDEX, Sluift::globals.coreLibIndex); |