unity-scope-onlinemusic-0.1+14.04.20140306/0000755000015201777760000000000012306014762020551 5ustar pbusernogroup00000000000000unity-scope-onlinemusic-0.1+14.04.20140306/tests/0000755000015201777760000000000012306014762021713 5ustar pbusernogroup00000000000000unity-scope-onlinemusic-0.1+14.04.20140306/tests/CMakeLists.txt0000644000015201777760000000245012306014511024444 0ustar pbusernogroup00000000000000SET(TEST_SSS_PARSER_SOURCES SmartScopesParserTest.cpp ) SET(TEST_SSS_CLIENT_SOURCES SmartScopesClientTest.cpp ) INCLUDE_DIRECTORIES( ${UNITY_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Test_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/../src ${CMAKE_CURRENT_BINARY_DIR}/src ) QT5_WRAP_CPP(TEST_SSS_PARSER_MOC SmartScopesParserTest.h) QT5_WRAP_CPP(TEST_SSS_CLIENT_MOC SmartScopesClientTest.h) ADD_EXECUTABLE( test-sss-parser ${TEST_SSS_PARSER_SOURCES} ${TEST_SSS_PARSER_MOC} ) ADD_EXECUTABLE( test-sss-client ${TEST_SSS_CLIENT_SOURCES} ${TEST_SSS_CLIENT_MOC} ) QT5_USE_MODULES(test-sss-parser Test Core) QT5_USE_MODULES(test-sss-client Test Core) TARGET_LINK_LIBRARIES(test-sss-parser sss-parser-lib ${DEEQT_LIBRARIES} ${UNITY_LIBRARIES} ${Qt5Core_LIBRARIES} ${Qt5Test_LIBRARIES} ${UUID_LIBRARIES} ) TARGET_LINK_LIBRARIES(test-sss-client sss-parser-lib ${DEEQT_LIBRARIES} ${UNITY_LIBRARIES} ${Qt5Core_LIBRARIES} ${Qt5Test_LIBRARIES} ${Qt5Network_LIBRARIES} ${UUID_LIBRARIES} ) ADD_TEST(NAME TestSSSParser COMMAND test-sss-parser) ADD_TEST(NAME TestSSSClient COMMAND test-sss-client) unity-scope-onlinemusic-0.1+14.04.20140306/tests/SmartScopesClientTest.h0000644000015201777760000000163312306014511026321 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef TESTSMARTSCOPESCLIENT_H #define TESTSMARTSCOPESCLIENT_H #include class SmartScopesClientTest : public QObject { Q_OBJECT private Q_SLOTS: void buildSearchUrlTest(); }; #endif unity-scope-onlinemusic-0.1+14.04.20140306/tests/SmartScopesParserTest.h0000644000015201777760000000175012306014511026337 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef TESTSMARTSCOPESPARSER_H #define TESTSMARTSCOPESPARSER_H #include class SmartScopesParserTest : public QObject { Q_OBJECT private Q_SLOTS: void parseSearchResultLineTest(); void parseGenericPreviewTest(); void parseMusicPreviewTest(); }; #endif unity-scope-onlinemusic-0.1+14.04.20140306/tests/SmartScopesParserTest.cpp0000644000015201777760000001663312306014511026700 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #include "SmartScopesParserTest.h" #include #include #include #include #include "SmartScopesClient.h" #include "SmartScopesParser.h" #include "SmartScopesPreviewParser.h" void SmartScopesParserTest::parseSearchResultLineTest() { const QString input[] = { "{", // invalid json "{\"type:1\"}", // invalid type "{\"scopes\": [[\"searchin-scopes.scope\", \"server\"], [\"info-openweathermap.scopes\", \"server\"], [\"reference-exact.scope\", \"server\"], [\"reference-rest.scope\", \"server\"], [\"news-googlenews.scope\", \"server\"], [\"info-reddit.scope\", \"server\"], [\"news-yahoostock.scope\", \"server\"], [\"music-grooveshark.scope\", \"server\"], [\"music-soundcloud.scope\", \"server\"], [\"info-foursquare.scope\", \"server\"], [\"music-songkick.scope\", \"server\"], [\"more_suggestions-u1ms.scope\", \"server\"], [\"more_suggestions-amazon.scope\", \"server\"], [\"more_suggestions-etsy.scope\", \"server\"], [\"more_suggestions-skimlinks.scope\", \"server\"], [\"more_suggestions-ebay.scope\", \"server\"]], \"server_sid\": \"abcdef\", \"type\": \"recommendations\"}", "{\"info\": {\"info-foursquare.scope\": [{\"category\": 0, \"comment\": \"55 Basinghall St London\", \"title\": \"Imprima (LON)\", \"uri\": \"https://foursquare.com/v/imprima-lon/4b60\", \"icon_hint\": \"/usr/share/icons/unity-icon-theme/places/svg/result-info.svg\", \"mimetype\": \"text/html\", \"dnd_uri\": \"https://foursquare.com/v/imprima-lon/4b606badf964a520e6e429e3\", \"result_type\": 0, \"metadata\": {\"id\": \"foursquare:1fc9f552b1f9161ab81fbc0616234f9ff1bdeeee\"}}, {\"category\": 0, \"comment\": \"123 Buckingham Palace Rd London\", \"title\": \"LON-BEL-123 Gym\", \"uri\": \"https://foursquare.com/v/lonbel123-gym/4d9ac297674ca143ceb6c243\", \"icon_hint\": \"https://foursquare.com/img/categories_v2/building/gym_88.png\", \"mimetype\": \"text/html\", \"dnd_uri\": \"https://foursquare.com/v/lonbel123-gym/4d9ac297674ca143ceb6c243\", \"result_type\": 0, \"metadata\": {\"id\": \"foursquare:d86115198ba3a379740206d3c6da18220956a85c\"}}, {\"category\": 0, \"comment\": \"The Qube\", \"title\": \"HOK LON First Friday\", \"uri\": \"https://foursquare.com/v/hok-lon-first-friday/4f510633e4b044219085e211\", \"icon_hint\": \"https://foursquare.com/img/categories_v2/nightlife/default_88.png\", \"mimetype\": \"text/html\", \"dnd_uri\": \"https://foursquare.com/v/hok-lon-first-friday/4f510633e4b044219085e211\", \"result_type\": 0, \"metadata\": {\"id\": \"foursquare:e43c6843b4d3f0ab395a8b92e9f25b3be72e699f\"}}]}, \"type\": \"results\"}" }; SmartScopesParser parser; QList>> results; QString server_sid = ""; for (unsigned i=0; i res) { results.append(QPair>(scopeId, res)); }, [&server_sid](const QString &sid) { server_sid = sid; } ); } QCOMPARE(server_sid, QString("abcdef")); QCOMPARE(results.size(), 3); QCOMPARE(results[0].first, QString("info-foursquare.scope")); QCOMPARE(results[1].first, QString("info-foursquare.scope")); QCOMPARE(results[2].first, QString("info-foursquare.scope")); auto res = results[0].second; QCOMPARE(res->uri, QString("https://foursquare.com/v/imprima-lon/4b60")); QCOMPARE(res->title, QString("Imprima (LON)")); QCOMPARE(res->comment, QString("55 Basinghall St London")); QCOMPARE(res->icon_hint, QString("/usr/share/icons/unity-icon-theme/places/svg/result-info.svg")); QCOMPARE(res->dnd_uri, QString("https://foursquare.com/v/imprima-lon/4b606badf964a520e6e429e3")); res = results[1].second; QCOMPARE(res->uri, QString("https://foursquare.com/v/lonbel123-gym/4d9ac297674ca143ceb6c243")); QCOMPARE(res->title, QString("LON-BEL-123 Gym")); QCOMPARE(res->comment, QString("123 Buckingham Palace Rd London")); QCOMPARE(res->icon_hint, QString("https://foursquare.com/img/categories_v2/building/gym_88.png")); QCOMPARE(res->dnd_uri, QString("https://foursquare.com/v/lonbel123-gym/4d9ac297674ca143ceb6c243")); } void SmartScopesParserTest::parseGenericPreviewTest() { const QString input = "{\"info\": [{\"display_name\": \"Published\", \"icon_hint\": \"\", \"id\": \"Published\", \"value\": \"2013-05-07\"}], \"renderer_name\": \"preview-generic\", \"description\": \"The second exciting adventure in The Door Within trilogy.\", \"title\": \"The Rise of the Wyrm Lord\", \"actions\": [{\"display_name\": \"View\", \"icon_hint\": \"/usr/share/icons/unity-icon-theme/places/svg/service-googlebooks.svg\", \"id\": \"view\", \"activation_uri\": \"http://foo.com/abc\"}], \"subtitle\": \"Wayne Thomas Batson\", \"image_hint\": \"http://bks3.foo.com/books?id=R6B\"}"; SmartScopesPreviewParser parser; auto preview = parser.parse(input); QVERIFY(preview != nullptr); QVERIFY(UNITY_IS_GENERIC_PREVIEW(preview) != 0); QCOMPARE(unity_preview_get_title(preview), "The Rise of the Wyrm Lord"); QCOMPARE(unity_preview_get_subtitle(preview), "Wayne Thomas Batson"); unity_object_unref(preview); } void SmartScopesParserTest::parseMusicPreviewTest() { const QString input = "{\"info\": [], \"subtitle\": \"Los Mustang\", \"description\": \"\", \"title\": \"Los Mustang - Los Jovenes\", \"_format\": \"simple-serialization-v0\", \"attribution\": \"Certain content that appears in this preview comes from Ubuntu One Music Store. This content is provided 'as is' and is subject to change or removal at any time.\", \"actions\": [{\"icon_hint\": \"file:///usr/share/icons/unity-icon-theme/places/svg/service-u1.svg\", \"activation_uri\": \"https://foobar.com/x\", \"display_name\": \"Download\", \"extra_text\": \"9,99 USD\"}], \"tracks\": [{\"length\": 196, \"uri\": \"http://api.foobar.com/1\", \"track_no\": 1, \"title\": \"Los jovenes The young ones\"}, {\"length\": 149, \"uri\": \"http://api.foobar.com/2\", \"track_no\": 2, \"title\": \"Diana\"}, {\"length\": 162, \"uri\": \"http://api.7digital.com/3\", \"track_no\": 3, \"title\": \"Living doll\"}], \"renderer_name\": \"preview-music\", \"image_hint\": \"http://cdn.foobar.com/350.jpg\"}"; SmartScopesPreviewParser parser; auto preview = parser.parse(input); QVERIFY(preview != nullptr); QVERIFY(UNITY_IS_MUSIC_PREVIEW(preview) != 0); QCOMPARE(unity_preview_get_title(preview), "Los Mustang - Los Jovenes"); QCOMPARE(unity_preview_get_subtitle(preview), "Los Mustang"); unity_object_unref(preview); } QTEST_MAIN(SmartScopesParserTest) unity-scope-onlinemusic-0.1+14.04.20140306/tests/main.cpp0000644000015201777760000000004412306014511023331 0ustar pbusernogroup00000000000000int main(int argc, char **argv) { } unity-scope-onlinemusic-0.1+14.04.20140306/tests/SmartScopesClientTest.cpp0000644000015201777760000000420012306014511026645 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #include "SmartScopesClientTest.h" #include #include #include "SmartScopesClient.h" void SmartScopesClientTest::buildSearchUrlTest() { SmartScopesClient cli("pl"); QStringList scopes; scopes << "a.scope" << "b.scope"; QStringList removedScopes; removedScopes << "c.scope" << "d.scope"; auto url = cli.buildSearchUrl("foo", scopes, removedScopes, "abcdef-1234", "foobar.scope"); auto query = QUrlQuery(url.query()); QCOMPARE(query.queryItemValue("locale"), QString("pl")); QCOMPARE(query.queryItemValue("scopes"), QString("a.scope,b.scope")); QCOMPARE(query.queryItemValue("removed_scopes"), QString("c.scope,d.scope")); QCOMPARE(query.queryItemValue("session_id"), QString("abcdef-1234")); QCOMPARE(query.queryItemValue("origin"), QString("foobar.scope")); QCOMPARE(query.queryItemValue("q"), QString("foo")); url = cli.buildSearchUrl("bar", scopes, QStringList(), "abcdef-1234", "foobar.scope"); query = QUrlQuery(url.query()); QCOMPARE(query.queryItemValue("locale"), QString("pl")); QCOMPARE(query.queryItemValue("scopes"), QString("a.scope,b.scope")); QCOMPARE(query.queryItemValue("removed_scopes"), QString("")); QCOMPARE(query.queryItemValue("session_id"), QString("abcdef-1234")); QCOMPARE(query.queryItemValue("origin"), QString("foobar.scope")); QCOMPARE(query.queryItemValue("q"), QString("bar")); } QTEST_MAIN(SmartScopesClientTest) unity-scope-onlinemusic-0.1+14.04.20140306/CMakeLists.txt0000644000015201777760000000233512306014511023304 0ustar pbusernogroup00000000000000CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9) PROJECT(unity-scope-onlinemusic CXX) SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra") ADD_DEFINITIONS(-DQT_NO_KEYWORDS) FIND_PACKAGE(Gettext REQUIRED) FIND_PROGRAM(INTLTOOL_MERGE intltool-merge DOC "intltool-merge program") FIND_PACKAGE(Qt5Core REQUIRED) FIND_PACKAGE(Qt5Network REQUIRED) FIND_PACKAGE(Qt5Test REQUIRED) FIND_PACKAGE(PkgConfig REQUIRED) PKG_CHECK_MODULES(UNITY REQUIRED unity>=7.1.0) PKG_CHECK_MODULES(DEEQT REQUIRED libdee-qt5>=3.2) PKG_CHECK_MODULES(UUID REQUIRED uuid>=2.20.0) IF(${INTLTOOL_MERGE} MATCHES "INTLTOOL_MERGE-NOTFOUND") MESSAGE(FATAL_ERROR "xxx") ENDIF() INCLUDE(GNUInstallDirs) SET(ICON_DIR "${CMAKE_INSTALL_DATADIR}/icons/unity-icon-theme/places/svg") SET(CATEGORY_ICON_PATH "${ICON_DIR}/service-soundcloud.svg") SET(DEFAULT_ICON_PATH "${ICON_DIR}/result-music.svg") SET(GETTEXT_PACKAGE unity-scope-onlinemusic) SET(LOCALE_DIR "${CMAKE_INSTALL_DATADIR}/locale") CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/src/config.h" ) ENABLE_TESTING() ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(po) ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(data) unity-scope-onlinemusic-0.1+14.04.20140306/TODO.txt0000644000015201777760000000000012306014511022035 0ustar pbusernogroup00000000000000unity-scope-onlinemusic-0.1+14.04.20140306/po/0000755000015201777760000000000012306014762021167 5ustar pbusernogroup00000000000000unity-scope-onlinemusic-0.1+14.04.20140306/po/CMakeLists.txt0000644000015201777760000000126612306014511023724 0ustar pbusernogroup00000000000000FILE(GLOB SOURCE_FILES ${CMAKE_SOURCE_DIR}/src/*.cpp) SET(POT_FILE "unity-scope-onlinemusic.pot") ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${POT_FILE} COMMAND xgettext -o ${POT_FILE} --copyright-holder=\"Canonical Ltd. \" --package-name ${GETTEXT_PACKAGE} --qt --c++ --add-comments=TRANSLATORS --keyword=_ --keyword=tr --keyword=tr:1,2 --from-code=UTF-8 ${SOURCE_FILES} DEPENDS ${SOURCE_FILES}) ADD_CUSTOM_TARGET(pot DEPENDS ${POT_FILE}) FILE(GLOB PO_FILES *.po) GETTEXT_CREATE_TRANSLATIONS(${POT_FILE} ALL ${PO_FILES}) unity-scope-onlinemusic-0.1+14.04.20140306/src/0000755000015201777760000000000012306014762021340 5ustar pbusernogroup00000000000000unity-scope-onlinemusic-0.1+14.04.20140306/src/Utils.h0000644000015201777760000000231212306014511022577 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef SCOPEUTILS_H #define SCOPEUTILS_H #include #include #include #include GIcon* gfileIconFromUri(const QString &uri); GVariant* hashtableToAsv(GHashTable *hashtable); QVariantMap ghashtableToQVariantHash(GHashTable *hashtable); QVariantHash QVariantMapToHash(const QVariantMap &map); QString randomizedTimeUuid(); inline char* _(const char *__msgid) { return gettext(__msgid); } #endif unity-scope-onlinemusic-0.1+14.04.20140306/src/OnlineMusicScope.cpp0000644000015201777760000002211012306014511025247 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #include "OnlineMusicScope.h" #include "SmartScopesClient.h" #include "Utils.h" #include #include #include #include #include #include #include #include "config.h" const char* OnlineMusicScope::UNIQUE_NAME = "/com/canonical/unity/scope/onlinemusic"; const char* OnlineMusicScope::GROUP_NAME = "com.canonical.Unity.Scope.OnlineMusic"; extern "C" { int unity_scope_module_get_version() { return UNITY_SCOPE_API_VERSION; } GList *unity_scope_module_load_scopes(GError **/* error */) { bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR); textdomain(GETTEXT_PACKAGE); UnityAbstractScope *scope = OnlineMusicScope::instance().getUnityScope(); return g_list_append(NULL, scope); } } OnlineMusicScope::OnlineMusicScope() : m_disabledScopesSig(0) { // determine locale for Smart Scopes queries auto languages = g_get_language_names(); m_locale = (languages != nullptr && languages[0] != nullptr) ? QString(languages[0]) : QString("en"); int argc = 1; char *argv[1] = {nullptr}; // an instance of Application is needed to avoid warnings from Qt m_app = new QCoreApplication(argc, argv); setupUnityScope(); } OnlineMusicScope::~OnlineMusicScope() { if (m_disabledScopesSig > 0) { g_signal_handler_disconnect(unity_preferences_manager_get_default(), m_disabledScopesSig); } delete m_app; } void OnlineMusicScope::setupUnityScope() { g_set_prgname("unity-scope-onlinemusic"); // set program name for better error messages updateDisabledScopes(); auto prefs = unity_preferences_manager_get_default(); m_disabledScopesSig = g_signal_connect(prefs, "notify::disabled-scopes", G_CALLBACK(disabled_scopes_changed), this); m_unityScope = unity_simple_scope_new(); unity_simple_scope_set_group_name(m_unityScope, GROUP_NAME); unity_simple_scope_set_unique_name(m_unityScope, UNIQUE_NAME); auto icon = g_themed_icon_new(CATEGORY_ICON_PATH); auto cat = unity_category_new("popular", _("Popular online"), icon, UNITY_CATEGORY_RENDERER_VERTICAL_TILE); auto cats = unity_category_set_new(); unity_category_set_add(cats, cat); cat = unity_category_new("online", _("Online"), icon, UNITY_CATEGORY_RENDERER_VERTICAL_TILE); unity_category_set_add(cats, cat); unity_simple_scope_set_category_set(m_unityScope, cats); unity_simple_scope_set_search_async_func(m_unityScope, search_func, reinterpret_cast(this), nullptr); unity_simple_scope_set_preview_async_func(m_unityScope, preview_func, reinterpret_cast(this), nullptr); g_object_unref(icon); unity_object_unref(cat); unity_object_unref(cats); } OnlineMusicScope& OnlineMusicScope::instance() { static OnlineMusicScope m_scope; return m_scope; } UnityAbstractScope* OnlineMusicScope::getUnityScope() { return UNITY_ABSTRACT_SCOPE(m_unityScope); } UnityAbstractPreview *OnlineMusicScope::previewFunc(UnityResultPreviewer *previewer) { gpointer sessionId = g_hash_table_lookup(previewer->result.metadata, "session_id"); if (sessionId == nullptr) { qWarning() << "Missing 'session_id'"; return nullptr; } gpointer serverSid = g_hash_table_lookup(previewer->result.metadata, "server_sid"); if (serverSid == nullptr) { qWarning() << "Missing 'server_sid'"; return nullptr; } gpointer resultId = g_hash_table_lookup(previewer->result.metadata, "id"); if (resultId == nullptr) { qWarning() << "Missing result id"; return nullptr; } SmartScopesClient sss_client(m_locale); auto preview = sss_client.preview(QString(g_variant_get_string(static_cast(serverSid), nullptr)), QString(g_variant_get_string(static_cast(sessionId), nullptr)), QString(g_variant_get_string(static_cast(resultId), nullptr)), previewer->result); return preview; } void OnlineMusicScope::appendIfEnabled(QStringList &scopeToQuery, const QString &scopeId) const { if (!m_disabledScopes.contains(scopeId)) { scopeToQuery << scopeId; } } void OnlineMusicScope::searchFunc(UnityScopeSearchBase* search) { // no search results in Home if (search->search_context->search_type == UNITY_SEARCH_TYPE_GLOBAL) return; QString serverSid; QString sessionId = randomizedTimeUuid(); const QString query = QString(search->search_context->search_query).trimmed(); unsigned cat = query.isEmpty() ? Category::POPULAR_ONLINE : Category::ONLINE; QStringList scopes; if (query.isEmpty()) { appendIfEnabled(scopes, "more_suggestions-populartracks.scope"); } else { appendIfEnabled(scopes, "more_suggestions-u1ms.scope"); } appendIfEnabled(scopes, "music-grooveshark.scope"); appendIfEnabled(scopes, "music-soundcloud.scope"); if (scopes.size() == 0) // no scopes to query? return; // maps scopeId to list of results std::map>> results; SmartScopesClient sss_client(m_locale); sss_client.search(query, sessionId, scopes, QStringList(), "music-onlinemusic.scope", [search, sessionId, cat, &serverSid, &results](const QString& scopeId, std::shared_ptr result) { if (!serverSid.isEmpty()) { result->category = cat; result->metadata.insert(QString("server_sid"), serverSid); result->metadata.insert(QString("session_id"), sessionId); if (results.find(scopeId) == results.end()) { results[scopeId] = std::list>(); } results[scopeId].push_back(result); } }, [&serverSid](const QString &sid) { serverSid = sid; }, search->search_context->cancellable); // redistribute results from all scopes (scopeA, scopeB, scopeA, scopeB ...) bool haveData = false; do { haveData = false; for (auto &r: results) // iterate over (scope, resultlist) pairs { auto &reslist = r.second; haveData = haveData || (!reslist.empty()); if (!reslist.empty()) { // pop first result of this scope and add it to the output result set auto result = reslist.front(); reslist.pop_front(); auto unityResult = unity_scope_result_create_from_variant(result->toGVariant()); unity_result_set_add_result(search->search_context->result_set, unityResult); unity_scope_result_free(unityResult); } } } while (haveData); if (serverSid.isEmpty()) { qWarning() << "server_sid is missing in the reply"; } } void OnlineMusicScope::updateDisabledScopes() { int count = 0; m_disabledScopes.clear(); auto prefs = unity_preferences_manager_get_default(); if (prefs) { auto disabled = unity_preferences_manager_get_disabled_scopes(prefs, &count); for (int i = 0; i(user_data); scope->searchFunc(search); cb(search, cb_target); } void OnlineMusicScope::preview_func(UnityResultPreviewer *previewer, UnityAbstractPreviewCallback cb, void *cb_target, void *user_data) { auto scope = static_cast(user_data); auto preview = scope->previewFunc(previewer); cb(previewer, preview, cb_target); } void OnlineMusicScope::disabled_scopes_changed(GObject* /* obj */, GParamSpec* /*spec*/, gpointer user_data) { qDebug() << "disabled-scopes changed"; auto scope = static_cast(user_data); scope->updateDisabledScopes(); } unity-scope-onlinemusic-0.1+14.04.20140306/src/SmartScopesPreviewParser.h0000644000015201777760000000265012306014511026466 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef SMARTSCOPESPREVIEWPARSER_H #define SMARTSCOPESPREVIEWPARSER_H #include #include #include class SmartScopesPreviewParser { public: UnityPreview* parse(const QString &jsonText); private: UnityPreview* parseGenericPreview(const QJsonObject& root); UnityPreview* parseMusicPreview(const QJsonObject& root); void getBaseAttributes(const QJsonObject& root, QString &title, QString& subtitle, QString &description, GIcon **icon, QString &attribution); void getInfoHints(const QJsonObject& root, UnityPreview *preview); void getActions(const QJsonObject& root, UnityPreview *preview); void getTracks(const QJsonObject& root, UnityMusicPreview *preview); }; #endif unity-scope-onlinemusic-0.1+14.04.20140306/src/SmartScopesParser.cpp0000644000015201777760000001143612306014511025461 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #include "SmartScopesParser.h" #include "Utils.h" #include #include #include #include #include void SmartScopesParser::parseLine(const QString& text, const ResultHandler& resultHandler, const RecommendationsHandler &recommendationsHandler) { const QJsonDocument doc = QJsonDocument::fromJson(text.toUtf8()); const QJsonObject root = doc.object(); auto it = root.find("type"); if (it == root.end()) { qWarning() << "Missing 'type' element"; return; } const QString repType = it.value().toString(); if (repType == "results") handleResults(root, resultHandler); else if (repType == "recommendations") handleRecommendations(root, recommendationsHandler); else qWarning() << "Unsupported reply type:" << repType; } void SmartScopesParser::handleResults(const QJsonObject& root, const ResultHandler& resultHandler) { auto it = root.find("info"); if (it == root.end()) { qWarning() << "Missing 'info' element"; return; } const QJsonObject infoObj = it.value().toObject(); for (auto infoIt = infoObj.begin(); infoIt != infoObj.end(); infoIt++) { const QString scopeId = infoIt.key(); const QJsonArray results = infoIt.value().toArray(); for (auto resultsIt = results.begin(); resultsIt != results.end(); resultsIt++) { auto result = std::shared_ptr(new SmartScopesResult()); const QJsonObject resultObj = (*resultsIt).toObject(); it = resultObj.find("uri"); if (it == resultObj.end()) continue; result->uri = it.value().toString(); it = resultObj.find("title"); if (it == resultObj.end()) continue; result->title = it.value().toString(); it = resultObj.find("metadata"); if (it == resultObj.end()) continue; const auto metadata = it.value().toObject().toVariantMap(); // convert qvariant map returned by json parser to qvariant hash; // this is needed because DeeListModel::DataFromVariant() doesn't support QVariantMap. result->metadata = QVariantMapToHash(metadata); it = resultObj.find("icon_hint"); if (it == resultObj.end()) continue; result->icon_hint = it.value().toString(); it = resultObj.find("mimetype"); if (it == resultObj.end()) result->mimetype = "text/html"; else result->mimetype = it.value().toString(); it = resultObj.find("comment"); if (it != resultObj.end()) result->comment = it.value().toString(); it = resultObj.find("dnd_uri"); if (it != resultObj.end()) result->dnd_uri = it.value().toString(); else result->dnd_uri = ""; resultHandler(scopeId, result); } } } void SmartScopesParser::handleRecommendations(const QJsonObject& root, const RecommendationsHandler& recommendationsHandler) { auto it = root.find("server_sid"); if (it == root.end()) { qWarning() << "Missing 'server_sid' element"; return; } auto server_sid = it.value().toString(); recommendationsHandler(server_sid); } GVariant* SmartScopesResult::toGVariant() const { auto metavar = DeeListModel::DataFromVariant(metadata); GVariant* children[] = { g_variant_new_string(uri.toUtf8().constData()), g_variant_new_string(icon_hint.toUtf8().constData()), g_variant_new_uint32(category), g_variant_new_uint32(resultType), g_variant_new_string(mimetype.toUtf8().constData()), g_variant_new_string(title.toUtf8().constData()), g_variant_new_string(comment.toUtf8().constData()), g_variant_new_string(dnd_uri.toUtf8().constData()), metavar }; auto array = g_variant_new_tuple(children, sizeof(children)/sizeof(GVariant*)); return array; } unity-scope-onlinemusic-0.1+14.04.20140306/src/CMakeLists.txt0000644000015201777760000000226012306014511024070 0ustar pbusernogroup00000000000000INCLUDE_DIRECTORIES( ${UNITY_INCLUDE_DIRS} ${DEEQT_INCLUDE_DIRS} ${Qt5Core_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${UUID_INCLUDE_DIRS} ) SET(SSS_PARSER_SOURCES SmartScopesParser.cpp SmartScopesPreviewParser.cpp SmartScopesClient.cpp Utils.cpp NetworkRequestThread.cpp ) SET(SCOPE_SOURCES OnlineMusicScope.cpp ) QT5_WRAP_CPP(SSS_PARSER_SOURCES_MOC NetworkRequestThread.h) ADD_LIBRARY(sss-parser-lib STATIC ${SSS_PARSER_SOURCES} ${SSS_PARSER_SOURCES_MOC} ) SET_TARGET_PROPERTIES(sss-parser-lib PROPERTIES COMPILE_FLAGS -fPIC) ADD_LIBRARY(unity-scope-onlinemusic MODULE ${SCOPE_SOURCES} ${SCOPE_SOURCES_MOC} ) SET_TARGET_PROPERTIES(unity-scope-onlinemusic PROPERTIES PREFIX "") TARGET_LINK_LIBRARIES(unity-scope-onlinemusic ${UNITY_LIBRARIES} sss-parser-lib ${DEEQT_LIBRARIES} ${Qt5Core_LIBRARIES} ${Qt5Network_LIBRARIES} ${UUID_LIBRARIES} ) INSTALL( TARGETS unity-scope-onlinemusic LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/unity" ) unity-scope-onlinemusic-0.1+14.04.20140306/src/SmartScopesClient.h0000644000015201777760000000416312306014511025107 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef SMARTSCOPESCLIENT_H #define SMARTSCOPESCLIENT_H #include #include #include #include "SmartScopesParser.h" #include "SmartScopesPreviewParser.h" class StringList; class SmartScopesClient { public: SmartScopesClient(const QString &locale); ~SmartScopesClient(); void search(const QString& query, const QString& sessionId, const QStringList& scopes, const QStringList& removedScopes, const QString &origin, const ResultHandler& resultHandler, const RecommendationsHandler& recommendHandler, UnityCancellable *cancellable); UnityAbstractPreview* preview(const QString& serverSid, const QString& sessionId, const QString &resultId, const UnityScopeResult& result); QUrl buildSearchUrl(const QString& searchString, const QStringList& scopes, const QStringList& removedScopes, const QString& sessionId, const QString &origin); QUrl buildPreviewUrl(const QString& sessionId, const QString& resultId); private: static QByteArray resultToJson(const UnityScopeResult& result); static const QString SERVER; static const QString SEARCH_URI; static const QString PREVIEW_URI; SmartScopesParser m_parser; SmartScopesPreviewParser m_previewParser; QString m_locale; QString m_server; }; #endif unity-scope-onlinemusic-0.1+14.04.20140306/src/SmartScopesPreviewParser.cpp0000644000015201777760000002035612306014511027024 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #include "SmartScopesPreviewParser.h" #include "Utils.h" #include #include #include UnityPreview* SmartScopesPreviewParser::parse(const QString &jsonText) { const QJsonDocument doc = QJsonDocument::fromJson(jsonText.toUtf8()); const QJsonObject root = doc.object(); auto it = root.find("renderer_name"); if (it == root.end()) { qWarning() << "Missing 'renderer_name' element"; return nullptr; } auto renderer = it.value().toString(); if (renderer == "preview-music") { return parseMusicPreview(root); } else if (renderer == "preview-generic") { return parseGenericPreview(root); } qWarning() << "Unsupported renderer" << renderer << ", using generic preview"; return parseGenericPreview(root); } UnityPreview* SmartScopesPreviewParser::parseGenericPreview(const QJsonObject& root) { QString title, subtitle, description, attribution; GIcon *icon; getBaseAttributes(root, title, subtitle, description, &icon, attribution); auto preview = unity_generic_preview_new(title.toUtf8().data(), description.toUtf8().data(), icon); g_object_unref(icon); unity_preview_set_subtitle(UNITY_PREVIEW(preview), subtitle.toUtf8().data()); getInfoHints(root, UNITY_PREVIEW(preview)); getActions(root, UNITY_PREVIEW(preview)); return UNITY_PREVIEW(preview); } UnityPreview* SmartScopesPreviewParser::parseMusicPreview(const QJsonObject& root) { QString title, subtitle, description, attribution; GIcon *icon; getBaseAttributes(root, title, subtitle, description, &icon, attribution); auto preview = unity_music_preview_new(title.toUtf8().data(), subtitle.toUtf8().data(), icon); g_object_unref(icon); getInfoHints(root, UNITY_PREVIEW(preview)); getActions(root, UNITY_PREVIEW(preview)); getTracks(root, preview); return UNITY_PREVIEW(preview); } void SmartScopesPreviewParser::getBaseAttributes(const QJsonObject& root, QString &title, QString& subtitle, QString &description, GIcon **icon, QString &attribution) { auto it = root.find("title"); if (it != root.end()) title = it.value().toString(); it = root.find("subtitle"); if (it != root.end()) subtitle = it.value().toString(); it = root.find("description_html"); if (it != root.end()) { description = it.value().toString(); } else { it = root.find("description"); if (it != root.end()) description = it.value().toString(); } *icon = nullptr; it = root.find("image_hint"); if (it != root.end()) { auto iconUri = it.value().toString(); auto iconFile = g_file_new_for_uri(iconUri.toUtf8().data()); *icon = g_file_icon_new(iconFile); g_object_unref(iconFile); } it = root.find("attribution"); if (it != root.end()) { attribution = it.value().toString(); if (!attribution.isEmpty()) { auto escaped = g_markup_escape_text(attribution.toUtf8().data(), -1); //FIXME: is there a more Qt'ish way to do that? attribution = "\n──────────────\n" + QString(escaped) + ""; g_free(escaped); } } } void SmartScopesPreviewParser::getInfoHints(const QJsonObject& root, UnityPreview *preview) { auto it = root.find("info"); if (it != root.end()) { const QJsonArray hints = it.value().toArray(); for (auto hintIt = hints.begin(); hintIt != hints.end(); hintIt++) { const QJsonObject hintObj = (*hintIt).toObject(); QString id, displayName, value; GIcon *icon = nullptr; auto hintPropIt = hintObj.find("id"); if (hintPropIt == hintObj.end()) continue; id = hintPropIt.value().toString(); hintPropIt = hintObj.find("display_name"); if (hintPropIt == hintObj.end()) continue; displayName = hintPropIt.value().toString(); hintPropIt = hintObj.find("value"); if (hintPropIt == hintObj.end()) continue; value = hintPropIt.value().toString(); hintPropIt = hintObj.find("icon_hint"); if (hintPropIt != hintObj.end()) icon = gfileIconFromUri(hintPropIt.value().toString()); auto info = unity_info_hint_new(id.toUtf8().data(), displayName.toUtf8().data(), icon, value.toUtf8().data()); unity_preview_add_info(preview, info); } } } void SmartScopesPreviewParser::getActions(const QJsonObject& root, UnityPreview *preview) { auto it = root.find("actions"); if (it != root.end()) { const QJsonArray actions = it.value().toArray(); for (auto actionIt = actions.begin(); actionIt != actions.end(); actionIt++) { const QJsonObject actionObj = (*actionIt).toObject(); QString uri, displayName; GIcon *icon = nullptr; auto actionPropIt = actionObj.find("activation_uri"); if (actionPropIt == actionObj.end()) continue; uri = actionPropIt.value().toString(); actionPropIt = actionObj.find("display_name"); if (actionPropIt == actionObj.end()) continue; displayName = actionPropIt.value().toString(); actionPropIt = actionObj.find("icon_hint"); if (actionPropIt != actionObj.end()) icon = gfileIconFromUri(actionPropIt.value().toString()); auto action = unity_preview_action_new(uri.toUtf8().data(), displayName.toUtf8().data(), icon); actionPropIt = actionObj.find("extra_text"); if (actionPropIt != actionObj.end()) unity_preview_action_set_extra_text(action, actionPropIt.value().toString().toUtf8().data()); unity_preview_add_action(preview, action); } } } void SmartScopesPreviewParser::getTracks(const QJsonObject& root, UnityMusicPreview *preview) { auto it = root.find("tracks"); if (it != root.end()) { const QJsonArray tracks = it.value().toArray(); for (auto trackIt = tracks.begin(); trackIt != tracks.end(); trackIt++) { const QJsonObject trackObj = (*trackIt).toObject(); QString uri, title; int trackNo, length; auto trackPropIt = trackObj.find("uri"); if (trackPropIt == trackObj.end()) continue; uri = trackPropIt.value().toString(); trackPropIt = trackObj.find("title"); if (trackPropIt == trackObj.end()) continue; title = trackPropIt.value().toString(); trackPropIt = trackObj.find("track_no"); if (trackPropIt == trackObj.end()) continue; trackNo = trackPropIt.value().toDouble(); trackPropIt = trackObj.find("length"); if (trackPropIt == trackObj.end()) continue; length = trackPropIt.value().toDouble(); auto trackData = unity_track_metadata_new(); unity_track_metadata_set_uri(trackData, uri.toUtf8().data()); unity_track_metadata_set_title(trackData, title.toUtf8().data()); unity_track_metadata_set_track_no(trackData, trackNo); unity_track_metadata_set_length(trackData, length); unity_music_preview_add_track(preview, trackData); } } } unity-scope-onlinemusic-0.1+14.04.20140306/src/OnlineMusicScope.h0000644000015201777760000000401512306014511024720 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef ONLINEMUSICSCOPE_H #define ONLINEMUSICSCOPE_H #include #include #include #include class QCoreApplication; enum Category { POPULAR_ONLINE, ONLINE }; class OnlineMusicScope { public: static OnlineMusicScope& instance(); UnityAbstractScope* getUnityScope(); ~OnlineMusicScope(); private: OnlineMusicScope(); void setupUnityScope(); void appendIfEnabled(QStringList &scopeToQuery, const QString &scopeId) const; static void search_func(UnityScopeSearchBase* search, UnityScopeSearchBaseCallback cb, void* cb_target, void* user_data); static void preview_func(UnityResultPreviewer *previewer, UnityAbstractPreviewCallback cb, void *cb_target, void *user_data); static void disabled_scopes_changed(GObject *obj, GParamSpec *spec, gpointer user_data); UnityAbstractPreview * previewFunc(UnityResultPreviewer *previewer); void searchFunc(UnityScopeSearchBase* search); void updateDisabledScopes(); static const char* UNIQUE_NAME; static const char* GROUP_NAME; static const char* SSS_SCOPE_ID; UnitySimpleScope* m_unityScope; QCoreApplication *m_app; QString m_locale; QSet m_disabledScopes; gulong m_disabledScopesSig; }; #endif unity-scope-onlinemusic-0.1+14.04.20140306/src/NetworkRequestThread.h0000644000015201777760000000340312306014511025633 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef NETWORKREQUESTTHREAD_H #define NETWORKREQUESTTHREAD_H #include #include #include #include #include #include #include class QNetworkReply; class QNetworkAccessManager; typedef QPair HttpHeader; typedef QList HttpHeadersList; class NetworkRequestThread : public QThread { Q_OBJECT public: NetworkRequestThread(const QUrl &url, UnityCancellable *cancellable = nullptr, const HttpHeadersList& = HttpHeadersList()); ~NetworkRequestThread(); void run(); QNetworkReply* getReply() const; void cancel(); public Q_SLOTS: void queryDone(QNetworkReply *reply); private: static void onCancelled(GCancellable *cancellable, NetworkRequestThread *networkThread); QUrl m_url; GCancellable *m_cancellable; gulong m_cancelSigId; HttpHeadersList m_headers; mutable QMutex m_mutex; QNetworkReply *m_reply; QNetworkAccessManager *m_manager; }; #endif unity-scope-onlinemusic-0.1+14.04.20140306/src/config.h.in0000644000015201777760000000163412306014511023357 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef __CONFIG_H #define __CONFIG_H #define ICON_PATH "@ICON_PATH" #define CATEGORY_ICON_PATH "@CATEGORY_ICON_PATH@" #define GETTEXT_PACKAGE "@GETTEXT_PACKAGE@" #define LOCALE_DIR "@LOCALE_DIR@" #endif unity-scope-onlinemusic-0.1+14.04.20140306/src/Utils.cpp0000644000015201777760000000552712306014511023145 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #include "Utils.h" #include #include #include GIcon* gfileIconFromUri(const QString &uri) { auto iconFile = g_file_new_for_uri(uri.toUtf8().data()); auto icon = g_file_icon_new(iconFile); g_object_unref(iconFile); return icon; } GVariant* hashtableToAsv(GHashTable *hashtable) { GHashTableIter iter; gpointer key, value; std::vector children(g_hash_table_size(hashtable)); int n = 0; g_hash_table_iter_init(&iter, hashtable); while (g_hash_table_iter_next(&iter, &key, &value)) { children[n++] = g_variant_new_dict_entry(g_variant_new_string((gchar*)key), g_variant_new_variant((GVariant*)value)); } return g_variant_new_array(G_VARIANT_TYPE("{sv}"), children.data(), children.size()); } QVariantMap ghashtableToQVariantHash(GHashTable *hashtable) { QVariantMap vmap; GHashTableIter iter; gpointer key, value; g_hash_table_iter_init(&iter, hashtable); while (g_hash_table_iter_next(&iter, &key, &value)) { vmap.insert((gchar*)key, DeeListModel::VariantForData((GVariant*)value)); } return vmap; } /** Generate Time-UUID with node (MAC) bytes replaced with random bytes, so that it doesn't contain any sensitive data. */ QString randomizedTimeUuid() { char res[37]; unsigned char data1[16]; uuid_generate_time (data1); unsigned char data2[16]; uuid_generate_random (data2); // last 6 bytes are MAC, overwrite with random bytes and set multicast bit, see section 4.5 of RFC 4122. for (int i = 10; i<16; i++) data1[i] = data2[i]; data1[10] |= 1; uuid_unparse (data1, res); return QString(res); } QVariantHash QVariantMapToHash(const QVariantMap &map) { auto result = QVariantHash(); for (auto it = map.constBegin(); it != map.constEnd(); ++it) { if ((*it).type() == QVariant::Map) { auto val = QVariantMapToHash((*it).toMap()); result.insert(it.key(), val); } else { result.insert(it.key(), it.value()); } } return result; } unity-scope-onlinemusic-0.1+14.04.20140306/src/SmartScopesParser.h0000644000015201777760000000324712306014511025127 0ustar pbusernogroup00000000000000/* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #ifndef SMARTSCOPESPARSER_H #define SMARTSCOPESPARSER_H #include #include #include #include #include struct SmartScopesResult { QString uri; QString title; QString comment; QString mimetype; QString icon_hint; QString dnd_uri; unsigned int category; unsigned int resultType; QVariantHash metadata; GVariant* toGVariant() const; }; typedef std::function result)> ResultHandler; typedef std::function RecommendationsHandler; class SmartScopesParser { public: void parseLine(const QString& text, const ResultHandler& resultHandler, const RecommendationsHandler &recommendationsHandler); private: void handleResults(const QJsonObject& root, const ResultHandler& resultHandler); void handleRecommendations(const QJsonObject& root, const RecommendationsHandler &recommendationsHandler); }; #endif unity-scope-onlinemusic-0.1+14.04.20140306/src/NetworkRequestThread.cpp0000644000015201777760000000547212306014511026176 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #include "NetworkRequestThread.h" #include #include #include #include #include NetworkRequestThread::NetworkRequestThread(const QUrl &url, UnityCancellable *cancellable, const HttpHeadersList& headers) : QThread(), m_url(url), m_cancellable(nullptr), m_cancelSigId(0), m_headers(headers), m_reply(nullptr), m_manager(nullptr) { if (cancellable != nullptr) { m_cancellable = unity_cancellable_get_gcancellable(cancellable); } } NetworkRequestThread::~NetworkRequestThread() { if (m_manager) m_manager->deleteLater(); if (m_reply) m_reply->deleteLater(); if (m_cancellable && m_cancelSigId > 0) g_cancellable_disconnect(m_cancellable, m_cancelSigId); } void NetworkRequestThread::run() { qDebug() << "Remote request:" << m_url; QMutexLocker lock(&m_mutex); // m_reply has to be locked till processing ends if (m_cancellable) { m_cancelSigId = g_cancellable_connect(m_cancellable, G_CALLBACK(onCancelled), this, nullptr); if (m_cancelSigId == 0) return; } m_manager = new QNetworkAccessManager(); QNetworkRequest request(m_url); for (auto it = m_headers.begin(); it != m_headers.end(); it++) { request.setRawHeader((*it).first, (*it).second); } connect(m_manager, SIGNAL(finished(QNetworkReply *)), this, SLOT(queryDone(QNetworkReply *))); m_reply = m_manager->get(request); QThread::exec(); // enter event loop } QNetworkReply* NetworkRequestThread::getReply() const { QMutexLocker lock(&m_mutex); return m_reply; } void NetworkRequestThread::queryDone(QNetworkReply* /* reply */) { quit(); } void NetworkRequestThread::cancel() { if (m_reply) { qDebug() << "Search request canceled:" << m_url; m_reply->abort(); quit(); } } void NetworkRequestThread::onCancelled(GCancellable * /* cancellable */, NetworkRequestThread *networkThread) { networkThread->cancel(); } unity-scope-onlinemusic-0.1+14.04.20140306/src/SmartScopesClient.cpp0000644000015201777760000001323612306014511025443 0ustar pbusernogroup00000000000000/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* * Copyright (C) 2013 Canonical, Ltd. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, 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 this program. If not, see . * * Author: Pawel Stolowski */ #include "SmartScopesClient.h" #include #include #include #include #include #include #include #include #include #include #include #include "Utils.h" #include "NetworkRequestThread.h" const QString SmartScopesClient::SERVER = "https://productsearch.ubuntu.com"; const QString SmartScopesClient::SEARCH_URI = "/smartscopes/v1/search"; const QString SmartScopesClient::PREVIEW_URI = "/smartscopes/v1/preview"; SmartScopesClient::SmartScopesClient(const QString &locale) : m_locale(locale) { m_server = QProcessEnvironment::systemEnvironment().value("SMART_SCOPES_SERVER", QString::null); if (m_server == QString::null) m_server = SERVER; } SmartScopesClient::~SmartScopesClient() { } void SmartScopesClient::search(const QString& query, const QString& sessionId, const QStringList& scopes, const QStringList& removedScopes, const QString &origin, const ResultHandler& resultHandler, const RecommendationsHandler& recommendHandler, UnityCancellable *cancellable) { auto url = buildSearchUrl(query, scopes, removedScopes, sessionId, origin); auto thread = new NetworkRequestThread(url, cancellable); QEventLoop loop; QObject::connect(thread, SIGNAL(finished()), &loop, SLOT(quit())); thread->start(); loop.exec(); thread->wait(); qDebug() << "Search request finished" << url; QNetworkReply* reply = thread->getReply(); Q_ASSERT(reply != nullptr); if (reply->error() == QNetworkReply::NoError) { if (cancellable != nullptr && unity_cancellable_is_cancelled(cancellable)) { return; } while (reply->canReadLine()) { auto line = reply->readLine(); m_parser.parseLine(line, resultHandler, recommendHandler); } } else { qWarning() << "Error processing search request:" << reply->errorString(); } thread->deleteLater(); } UnityAbstractPreview* SmartScopesClient::preview(const QString& serverSid, const QString& sessionId, const QString &resultId, const UnityScopeResult& result) { auto url = buildPreviewUrl(sessionId, resultId); auto encodedResult = resultToJson(result).replace("\r", "").replace("\n",""); HttpHeadersList headers; headers.append(HttpHeader("X-SERVER-SID", serverSid.toUtf8())); headers.append(HttpHeader("X-PREVIOUS-RESULT", encodedResult)); auto thread = new NetworkRequestThread(url, nullptr, headers); QEventLoop loop; QObject::connect(thread, SIGNAL(finished()), &loop, SLOT(quit())); thread->start(); loop.exec(); thread->wait(); QNetworkReply* reply = thread->getReply(); Q_ASSERT(reply != nullptr); UnityPreview *preview = nullptr; if (reply->error() == QNetworkReply::NoError) { auto data = reply->readAll(); qDebug() << "Got preview:" << data; preview = m_previewParser.parse(data); } else { qWarning() << "Error processing preview request:" << reply->errorString(); } thread->deleteLater(); return UNITY_ABSTRACT_PREVIEW(preview); } QUrl SmartScopesClient::buildSearchUrl(const QString &searchString, const QStringList& scopes, const QStringList& removedScopes, const QString& sessionId, const QString &origin) { QUrlQuery query; query.addQueryItem("q", searchString); query.addQueryItem("session_id", sessionId); query.addQueryItem("locale", m_locale); if (scopes.size() > 0) { query.addQueryItem("scopes", scopes.join(",")); } if (removedScopes.size() > 0) { query.addQueryItem("removed_scopes", removedScopes.join(",")); } if (!origin.isEmpty()) { query.addQueryItem("origin", origin); } QUrl url(QString("%1%2").arg(m_server).arg(SEARCH_URI)); url.setQuery(query); return url; } QUrl SmartScopesClient::buildPreviewUrl(const QString& sessionId, const QString& resultId) { QUrlQuery query; query.addQueryItem("session_id", sessionId); query.addQueryItem("result_id", resultId); query.addQueryItem("locale", m_locale); QUrl url(QString("%1%2").arg(m_server).arg(PREVIEW_URI)); url.setQuery(query); return url; } QByteArray SmartScopesClient::resultToJson(const UnityScopeResult& result) { QVariantMap vmap; vmap.insert("uri", result.uri); vmap.insert("title", result.title); vmap.insert("icon_hint", result.icon_hint); vmap.insert("comment", result.comment); vmap.insert("metadata", ghashtableToQVariantHash(result.metadata)); const QJsonDocument doc = QJsonDocument::fromVariant(vmap); return doc.toJson(); } unity-scope-onlinemusic-0.1+14.04.20140306/data/0000755000015201777760000000000012306014762021462 5ustar pbusernogroup00000000000000unity-scope-onlinemusic-0.1+14.04.20140306/data/CMakeLists.txt0000644000015201777760000000127012306014511024212 0ustar pbusernogroup00000000000000CONFIGURE_FILE( unity-scope-onlinemusic.service.in unity-scope-onlinemusic.service ) CONFIGURE_FILE( onlinemusic.scope.in.in onlinemusic.scope.in ) ADD_CUSTOM_COMMAND(OUTPUT onlinemusic.scope COMMAND ${INTLTOOL_MERGE} -d -u ../po onlinemusic.scope.in onlinemusic.scope DEPENDS onlinemusic.scope.in ) ADD_CUSTOM_TARGET(scopedotfile ALL DEPENDS onlinemusic.scope) INSTALL( FILES "${CMAKE_CURRENT_BINARY_DIR}/unity-scope-onlinemusic.service" DESTINATION "${CMAKE_INSTALL_DATADIR}/dbus-1/services" ) INSTALL( FILES "${CMAKE_CURRENT_BINARY_DIR}/onlinemusic.scope" DESTINATION "${CMAKE_INSTALL_DATADIR}/unity/scopes/music" ) unity-scope-onlinemusic-0.1+14.04.20140306/data/onlinemusic.scope.in.in0000644000015201777760000000126212306014511026045 0ustar pbusernogroup00000000000000[Scope] DBusName=com.canonical.Unity.Scope.OnlineMusic DBusPath=/com/canonical/unity/scope/onlinemusic Module=@CMAKE_INSTALL_FULL_LIBDIR@/unity/unity-scope-onlinemusic.so ModuleType=C Type=music RequiredMetadata= OptionalMetadata= _Name=Online music Icon=/usr/share/unity/icons/lens-nav-music.svg _Description=This is an Ubuntu search plugin that enables information from remote music sources to be searched and displayed in the Dash underneath the Music header. If you do not wish to search this content source, you can disable this search plugin. _SearchHint=Search music collection GlobalSearches=false RemoteContent=true [Desktop Entry] X-Ubuntu-Gettext-Domain=unity-scope-onlinemusic unity-scope-onlinemusic-0.1+14.04.20140306/data/unity-scope-onlinemusic.service.in0000644000015201777760000000020712306014511030242 0ustar pbusernogroup00000000000000[D-BUS Service] Name=com.canonical.Unity.Scope.OnlineMusic Exec=@CMAKE_INSTALL_FULL_BINDIR@/unity-scope-loader music/onlinemusic.scope