unity-scope-deviantart-0.1+13.10.20130723/0000755000015700001700000000000012173420465020325 5ustar pbuserpbgroup00000000000000unity-scope-deviantart-0.1+13.10.20130723/po/0000755000015700001700000000000012173420465020743 5ustar pbuserpbgroup00000000000000unity-scope-deviantart-0.1+13.10.20130723/po/unity-scope-deviantart.pot0000644000015700001700000000220512173417272026106 0ustar pbuserpbgroup00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-02-21 01:55+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: ../src/unity_deviantart_daemon.py:33 ../data/deviantart.scope.in.h:4 msgid "Search deviantART" msgstr "" #: ../src/unity_deviantart_daemon.py:34 msgid "Sorry, there are no deviantART results that match your search." msgstr "" #: ../src/unity_deviantart_daemon.py:35 msgid "Powered by deviantART" msgstr "" #: ../src/unity_deviantart_daemon.py:43 ../data/deviantart.scope.in.h:2 msgid "deviantART" msgstr "" #: ../data/deviantart.scope.in.h:1 msgid "art;deviantart;image;da;deviant;" msgstr "" #: ../data/deviantart.scope.in.h:3 msgid "Find deviantART art" msgstr "" unity-scope-deviantart-0.1+13.10.20130723/po/POTFILES.in0000644000015700001700000000013512173417272022521 0ustar pbuserpbgroup00000000000000[encoding: UTF-8] src/unity_deviantart_daemon.py [type: gettext/ini]data/deviantart.scope.in unity-scope-deviantart-0.1+13.10.20130723/MANIFEST.in0000644000015700001700000000007612173417272022070 0ustar pbuserpbgroup00000000000000include MANIFEST.in include src/* include data/* include po/* unity-scope-deviantart-0.1+13.10.20130723/tests/0000755000015700001700000000000012173420465021467 5ustar pbuserpbgroup00000000000000unity-scope-deviantart-0.1+13.10.20130723/tests/test_deviantart.py0000644000015700001700000000422112173417272025242 0ustar pbuserpbgroup00000000000000#! /usr/bin/python3 # -*- coding: utf-8 -*- from gi.repository import Unity from unittest import TestCase import imp class ResultSet(Unity.ResultSet): def __init__(self): Unity.ResultSet.__init__(self) self.results = [] def do_add_result(self, result): self.results.append({'uri':result.uri, 'title':result.title, 'comment':result.comment, 'icon':result.icon_hint}) class ScopeTestCase(TestCase): def init_scope(self, scope_path): self.scope_module = imp.load_source('scope', scope_path) self.scope = self.scope_module.load_scope() def perform_query(self, query, filter_set = Unity.FilterSet.new()): result_set = ResultSet() ctx = Unity.SearchContext.create(query, 0, filter_set, None, result_set, None) s = self.scope.create_search_for_query(ctx) s.run() return result_set class TestDeviantart(ScopeTestCase): def setUp(self): self.init_scope('src/unity_deviantart_daemon.py') def tearDown(self): self.scope = None self.scope_module = None def test_valid_searches(self): self.scope_module.SEARCH_URI = 'file:tests/data/mock_deviantart_pass#' expected_results = ['Asus eeeBox Heaven And Earth', 'https://th07.deviantart.net/fs70/150/f/2013/016/2/d/asus_eeebox_heaven_and_earth_by_mmesantos1-d5roikz.png'] results = [] for s in ['danrabbit']: result_set = self.perform_query(s) results.append(result_set.results[0]['title']) results.append(result_set.results[0]['icon']) self.assertEqual(results, expected_results) self.assertEqual(result_set.results[0]['comment'].startswith('OS: Linux Mint XFCE 14 x86'), True) def test_failing_search(self): self.scope_module.SEARCH_URI = 'file:tests/data/mock_deviantart_fail#' for s in ['danrabbit']: result_set = self.perform_query(s) self.assertEqual(len(result_set.results), 0) if __name__ == '__main__': unittest.main() unity-scope-deviantart-0.1+13.10.20130723/tests/data/0000755000015700001700000000000012173420465022400 5ustar pbuserpbgroup00000000000000unity-scope-deviantart-0.1+13.10.20130723/tests/data/mock_deviantart_fail0000644000015700001700000000000612173417272026466 0ustar pbuserpbgroup00000000000000 unity-scope-deviantart-0.1+13.10.20130723/tests/data/mock_deviantart_pass0000644000015700001700000063066512173417272026545 0ustar pbuserpbgroup00000000000000 deviantART: Newest Danrabbit http://browse.deviantart.com/?order=5&q=danrabbit deviantART RSS for sort:time danrabbit en-us Copyright 2013, deviantART.com Wed, 23 Jan 2013 07:34:39 PST deviantART.com http://blogs.law.harvard.edu/tech/rss https://st.deviantart.net/minish/widgets/apple-touch-icon-precomposed.png unity-scope-deviantart-0.1+13.10.20130723/src/0000755000015700001700000000000012173420465021114 5ustar pbuserpbgroup00000000000000unity-scope-deviantart-0.1+13.10.20130723/src/unity_deviantart_daemon.py0000644000015700001700000002031112173417272026401 0ustar pbuserpbgroup00000000000000#! /usr/bin/python3 # -*- coding: utf-8 -*- # Copyright (C) 2013 David Callé # 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 . from gi.repository import Unity from gi.repository import Gio import feedparser import urllib.parse import urllib.request import gettext import re APP_NAME = 'unity-scope-deviantart' LOCAL_PATH = '/usr/share/locale/' gettext.bindtextdomain(APP_NAME, LOCAL_PATH) gettext.textdomain(APP_NAME) _ = gettext.gettext GROUP_NAME = 'com.canonical.Unity.Scope.Graphics.Deviantart' UNIQUE_PATH = '/com/canonical/unity/scope/graphics/deviantart' SEARCH_URI = 'https://backend.deviantart.com/' SEARCH_HINT = _('Search deviantART') NO_RESULTS_HINT = _('Sorry, there are no deviantART results that match your search.') PROVIDER_CREDITS = _('Powered by deviantART') SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/' PROVIDER_ICON = SVG_DIR+'service-deviantart.svg' DEFAULT_RESULT_ICON = SVG_DIR+'result-graphics.svg' DEFAULT_RESULT_MIMETYPE = 'text/html' DEFAULT_RESULT_TYPE = Unity.ResultType.DEFAULT c1 = {'id' :'top', 'name' :_('deviantART'), 'icon' :SVG_DIR+'group-installed.svg', 'renderer':Unity.CategoryRenderer.VERTICAL_TILE} CATEGORIES = [c1] FILTERS = [] m1 = {'id' :'tags', 'type' :'s', 'field':Unity.SchemaFieldType.OPTIONAL} m2 = {'id' :'author', 'type' :'s', 'field':Unity.SchemaFieldType.OPTIONAL} m3 = {'id' :'resource', 'type' :'s', 'field':Unity.SchemaFieldType.OPTIONAL} EXTRA_METADATA = [m1, m2, m3] def search(search, filters): ''' Any search method returning results as a list of tuples. Available tuple fields: uri (string) icon (string) title (string) comment (string) dnd_uri (string) mimetype (string) category (int) result_type (Unity ResultType) extras metadata fields (variant) ''' results = [] if not search: return results search = urllib.parse.quote(search) uri = "%srss.xml?type=deviation&limit=25&q=boost:popular+%s" % (SEARCH_URI, search) print(uri) try: feed = feedparser.parse(uri) except Exception as error: print(error) feed = None if not feed or not 'entries' in feed: return results for entry in feed['entries']: try: if entry is None: continue if entry['media_rating'] != "nonadult": continue results.append({'uri':entry['link'].replace('http://', 'https://'), 'icon':entry['media_thumbnail'][0]['url'].replace('http://', 'https://'), 'title':entry['title'], 'comment':entry['summary'], 'author':entry['media_copyright']['url'], 'tags':entry['tags'][0]['term'], 'resource':entry['media_content'][0]['url'].replace('http://', 'https://')}) except Exception as error: print("Skipping an invalid result", error) return results # Classes below this point establish communication # with Unity, you probably shouldn't modify them. class MySearch (Unity.ScopeSearchBase): def __init__(self, search_context): super (MySearch, self).__init__() self.set_search_context (search_context) def do_run (self): ''' Adds results to the model ''' try: result_set = self.search_context.result_set for i in search(self.search_context.search_query, self.search_context.filter_state): if not 'uri' in i or not i['uri'] or i['uri'] == '': continue if not 'icon' in i or not i['icon'] or i['icon'] == '': i['icon'] = DEFAULT_RESULT_ICON if not 'mimetype' in i or not i['mimetype'] or i['mimetype'] == '': i['mimetype'] = DEFAULT_RESULT_MIMETYPE if not 'result_type' in i or not i['result_type'] or i['result_type'] == '': i['result_type'] = DEFAULT_RESULT_TYPE if not 'category' in i or not i['category'] or i['category'] == '': i['category'] = 0 if not 'title' in i or not i['title']: i['title'] = '' if not 'comment' in i or not i['comment']: i['comment'] = '' if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '': i['dnd_uri'] = i['uri'] result_set.add_result(**i) except Exception as error: print (error) class Preview (Unity.ResultPreviewer): def strip_html_tags(self, data): data = data.replace('
', '\n') p = re.compile(r'<.*?>') return p.sub('', data) def do_run(self): comment = self.strip_html_tags(self.result.comment.strip()) preview = Unity.GenericPreview.new(self.result.title, comment, None) if self.result.metadata and 'tags' in self.result.metadata and self.result.metadata['tags'].get_string() != '': tags = self.result.metadata['tags'].get_string().split('/') if len(tags) > 0: preview.add_info(Unity.InfoHint.new("tags", _("Tags"), None, ', '.join(tags))) if self.result.metadata and 'resource' in self.result.metadata and self.result.metadata['resource'].get_string() != '': thumbnail = Gio.FileIcon.new(Gio.file_new_for_uri(self.result.metadata['resource'].get_string())) else: thumbnail = Gio.FileIcon.new(Gio.file_new_for_uri(self.result.icon_hint)) preview.props.image = thumbnail if self.result.metadata and 'author' in self.result.metadata and self.result.metadata['author'].get_string() != '': author = self.result.metadata['author'].get_string().split('://')[1].split('.deviantart')[0] preview.props.subtitle = _("By %s") % author icon = Gio.FileIcon.new (Gio.file_new_for_path(PROVIDER_ICON)) view_action = Unity.PreviewAction.new("view", _("View"), icon) preview.add_action(view_action) return preview class Scope (Unity.AbstractScope): def __init__(self): Unity.AbstractScope.__init__(self) def do_get_search_hint (self): return SEARCH_HINT def do_get_schema (self): ''' Adds specific metadata fields ''' schema = Unity.Schema.new () if EXTRA_METADATA: for m in EXTRA_METADATA: schema.add_field(m['id'], m['type'], m['field']) #FIXME should be REQUIRED for credits schema.add_field('provider_credits', 's', Unity.SchemaFieldType.OPTIONAL) return schema def do_get_categories (self): ''' Adds categories ''' cs = Unity.CategorySet.new () if CATEGORIES: for c in CATEGORIES: cat = Unity.Category.new (c['id'], c['name'], Gio.ThemedIcon.new(c['icon']), c['renderer']) cs.add (cat) return cs def do_get_filters (self): ''' Adds filters ''' fs = Unity.FilterSet.new () # if FILTERS: # return fs def do_get_group_name (self): return GROUP_NAME def do_get_unique_name (self): return UNIQUE_PATH def do_create_search_for_query (self, search_context): se = MySearch (search_context) return se def do_create_previewer(self, result, metadata): rp = Preview() rp.set_scope_result(result) rp.set_search_metadata(metadata) return rp def load_scope(): return Scope() unity-scope-deviantart-0.1+13.10.20130723/src/__init__.py0000644000015700001700000000000012173417272023215 0ustar pbuserpbgroup00000000000000unity-scope-deviantart-0.1+13.10.20130723/setup.py0000644000015700001700000000124412173417272022042 0ustar pbuserpbgroup00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- from distutils.core import setup from DistUtilsExtra.command import * setup(name='unity-scope-deviantart', version='0.1', author='David Callé', author_email='davidc@framli.eu', url='http://launchpad.net/ubuntu-scopes', license='GNU General Public License (GPL)', data_files=[ ('share/dbus-1/services', ['data/unity-scope-deviantart.service']), ('share/unity-scopes/deviantart', ['src/unity_deviantart_daemon.py']), ('share/unity-scopes/deviantart', ['src/__init__.py']), ], cmdclass={'build': build_extra.build_extra, 'build_i18n': build_i18n.build_i18n,}) unity-scope-deviantart-0.1+13.10.20130723/setup.cfg0000644000015700001700000000021512173417272022146 0ustar pbuserpbgroup00000000000000[build] i18n=True [build_i18n] domain=unity-scope-deviantart desktop_files=[('share/unity/scopes/graphics', ('data/deviantart.scope.in',))] unity-scope-deviantart-0.1+13.10.20130723/data/0000755000015700001700000000000012173420465021236 5ustar pbuserpbgroup00000000000000unity-scope-deviantart-0.1+13.10.20130723/data/unity-scope-deviantart.service0000644000015700001700000000024312173417272027237 0ustar pbuserpbgroup00000000000000[D-BUS Service] Name=com.canonical.Unity.Scope.Graphics.Deviantart Exec=/usr/bin/python3 /usr/share/unity-scopes/scope-runner-dbus.py -s graphics/deviantart.scope unity-scope-deviantart-0.1+13.10.20130723/data/icons/0000755000015700001700000000000012173420465022351 5ustar pbuserpbgroup00000000000000unity-scope-deviantart-0.1+13.10.20130723/data/icons/service-deviantart.svg0000644000015700001700000000671512173417272026704 0ustar pbuserpbgroup00000000000000 image/svg+xml unity-scope-deviantart-0.1+13.10.20130723/data/deviantart.scope.in0000644000015700001700000000146512173417272025047 0ustar pbuserpbgroup00000000000000[Scope] DBusName=com.canonical.Unity.Scope.Graphics.Deviantart DBusPath=/com/canonical/unity/scope/graphics/deviantart Icon=/usr/share/icons/unity-icon-theme/places/svg/service-deviantart.svg _Keywords=art;deviantart;image;da;deviant; RequiredMetadata= OptionalMetadata=published[s];author[s];tags[s]; Loader=/usr/share/unity-scopes/deviantart/unity_deviantart_daemon.py Module=deviantart.unity_deviantart_daemon ModuleType=python3 RemoteContent=true Type=graphics _Name=deviantART _Description=This is an Ubuntu search plugin that enables information from deviantART to be searched and displayed in the Dash underneath the Graphics header. If you do not wish to search this content source, you can disable this search plugin. _SearchHint=Search deviantART [Desktop Entry] X-Ubuntu-Gettext-Domain=unity-scope-deviantart