~submarine/unity-scope-tumblr/trunk

« back to all changes in this revision

Viewing changes to src/unity_tumblr_daemon.py

  • Committer: David Callé
  • Date: 2013-07-10 09:25:36 UTC
  • Revision ID: davidc@framli.eu-20130710092536-fli7ky8b1cv08kw8
Scope init

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/python3
 
2
# -*- coding: utf-8 -*-
 
3
 
 
4
# Copyright (C) 2013 name <email>
 
5
# This program is free software: you can redistribute it and/or modify it
 
6
# under the terms of the GNU General Public License version 3, as published
 
7
# by the Free Software Foundation.
 
8
 
9
# This program is distributed in the hope that it will be useful, but
 
10
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
11
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
12
# PURPOSE.  See the GNU General Public License for more details.
 
13
 
14
# You should have received a copy of the GNU General Public License along
 
15
# with this program.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
 
17
from gi.repository import Unity
 
18
from gi.repository import Gio
 
19
import json
 
20
import urllib.request
 
21
import urllib.parse
 
22
import gettext
 
23
import re
 
24
import requests
 
25
import oauthlib.oauth1
 
26
 
 
27
APP_NAME = 'unity-scope-tumblr'
 
28
LOCAL_PATH = '/usr/share/locale/'
 
29
gettext.bindtextdomain(APP_NAME, LOCAL_PATH)
 
30
gettext.textdomain(APP_NAME)
 
31
_ = gettext.gettext
 
32
 
 
33
GROUP_NAME = 'com.canonical.Unity.Scope.Graphics.Tumblr'
 
34
UNIQUE_PATH = '/com/canonical/unity/scope/graphics/tumblr'
 
35
SEARCH_URI = ''
 
36
SEARCH_HINT = _('Search Tumblr')
 
37
NO_RESULTS_HINT = _('Sorry, there are no Tumblr results that match your search.')
 
38
PROVIDER_CREDITS = _('Powered by Tumblr')
 
39
SVG_DIR = '/usr/share/icons/unity-icon-theme/places/svg/'
 
40
PROVIDER_ICON = SVG_DIR + 'service-tumblr.svg'
 
41
DEFAULT_RESULT_ICON = SVG_DIR + 'group-notes.svg'
 
42
DEFAULT_RESULT_MIMETYPE = 'text/html'
 
43
DEFAULT_RESULT_TYPE = Unity.ResultType.DEFAULT
 
44
 
 
45
c1 = {'id': 'Cat 1',
 
46
      'name': _('Posts'),
 
47
      'icon': SVG_DIR + 'group-installed.svg',
 
48
      'renderer': Unity.CategoryRenderer.VERTICAL_TILE}
 
49
c2 = {'id': 'Cat 2',
 
50
      'name': _('Cat 2'),
 
51
      'icon': SVG_DIR + 'group-installed.svg',
 
52
      'renderer': Unity.CategoryRenderer.VERTICAL_TILE}
 
53
CATEGORIES = [c1, c2]
 
54
 
 
55
FILTERS = []
 
56
#FIXME: Add filters
 
57
 
 
58
m1 = {'id': 'photo',
 
59
      'type': 's',
 
60
      'field': Unity.SchemaFieldType.OPTIONAL}
 
61
m2 = {'id': 'metadata2',
 
62
      'type': 's',
 
63
      'field': Unity.SchemaFieldType.OPTIONAL}
 
64
EXTRA_METADATA = [m1, m2]
 
65
 
 
66
 
 
67
 
 
68
def search(search, filters):
 
69
    '''
 
70
    Any search method returning results as a list of tuples.
 
71
    Available tuple fields:
 
72
    uri (string)
 
73
    icon (string)
 
74
    title (string)
 
75
    comment (string)
 
76
    dnd_uri (string)
 
77
    mimetype (string)
 
78
    category (int)
 
79
    result_type (Unity ResultType)
 
80
    extras metadata fields (string or int)
 
81
    '''
 
82
    results = []
 
83
    if not search or len(search) < 3:
 
84
        return results
 
85
    key = "IIHEZXn6tO0PMBbAcz0YFZ2uujM0ey2iUBd0JWleIRe4lYKdpk"
 
86
    blog = urllib.parse.quote(search.replace('#', ''))
 
87
    uri = "https://api.tumblr.com/v2/blog/%s.tumblr.com/posts?api_key=%s&limit=24" % (blog, key)
 
88
    if search.startswith('#'):
 
89
        uri = "http://api.tumblr.com/v2/tagged?tag=%s&filter=text&api_key=%s&limit=24" % (blog, key)
 
90
    print (uri)
 
91
    res = urllib.request.urlopen(uri).read()
 
92
    data = json.loads(res.decode('utf-8'))
 
93
    
 
94
    if search.startswith('#'):
 
95
        source = data['response']
 
96
    else:
 
97
        source = data['response']['posts']
 
98
    for e in source:
 
99
        icon = None
 
100
        photo = ''
 
101
        if 'photos' in e:
 
102
            for l in e['photos']:
 
103
                icon = l['alt_sizes'][-2]['url']
 
104
                photo = l['alt_sizes'][1]['url']
 
105
                break
 
106
        if not 'title' in e:
 
107
#            caption = e['caption']
 
108
#            cap_match = re.match('.*<p>.*>*([^<]+)<*.*</p>.*', caption)
 
109
#            if cap_match:
 
110
#                print (cap_match.group(0))
 
111
#                title = cap_match.group(1)
 
112
#            else:
 
113
                title = ''
 
114
        else:
 
115
            title = e['title']
 
116
        results.append({'uri': e['post_url'],
 
117
                        'icon': icon,
 
118
                        'title': title,
 
119
                        'photo': photo})
 
120
    return results
 
121
 
 
122
 
 
123
class Preview(Unity.ResultPreviewer):
 
124
 
 
125
    def do_run(self):
 
126
        preview = Unity.GenericPreview.new(self.result.title,
 
127
                                           self.result.comment,
 
128
                                           None)
 
129
#        if self.result.metadata['photo'].get_string() != '':
 
130
        im = Gio.FileIcon.new(Gio.file_new_for_uri(self.result.metadata['photo'].get_string()))
 
131
        preview.props.image = im
 
132
#        else:
 
133
#            preview.props.image_source_uri = self.result.icon
 
134
        icon = Gio.FileIcon.new(Gio.file_new_for_path(PROVIDER_ICON))
 
135
        view_action = Unity.PreviewAction.new('view',
 
136
                                              _('View'),
 
137
                                              icon)
 
138
        preview.add_action(view_action)
 
139
        return preview
 
140
 
 
141
 
 
142
# Classes below this point establish communication
 
143
# with Unity, you probably shouldn't modify them.
 
144
 
 
145
 
 
146
class MySearch(Unity.ScopeSearchBase):
 
147
    def __init__(self, search_context):
 
148
        super(MySearch, self).__init__()
 
149
        self.set_search_context(search_context)
 
150
 
 
151
    def do_run(self):
 
152
        '''
 
153
        Adds results to the model
 
154
        '''
 
155
        try:
 
156
            result_set = self.search_context.result_set
 
157
            for i in search(self.search_context.search_query,
 
158
                            self.search_context.filter_state):
 
159
                if not 'uri' in i or not i['uri'] or i['uri'] == '':
 
160
                    continue
 
161
                if not 'icon' in i or not i['icon'] or i['icon'] == '':
 
162
                    i['icon'] = DEFAULT_RESULT_ICON
 
163
                if not 'mimetype' in i or not i['mimetype'] or i['mimetype'] == '':
 
164
                    i['mimetype'] = DEFAULT_RESULT_MIMETYPE
 
165
                if not 'result_type' in i or not i['result_type'] or i['result_type'] == '':
 
166
                    i['result_type'] = DEFAULT_RESULT_TYPE
 
167
                if not 'category' in i or not i['category'] or i['category'] == '':
 
168
                    i['category'] = 0
 
169
                if not 'title' in i or not i['title']:
 
170
                    i['title'] = ''
 
171
                if not 'comment' in i or not i['comment']:
 
172
                    i['comment'] = ''
 
173
                if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '':
 
174
                    i['dnd_uri'] = i['uri']
 
175
                result_set.add_result(**i)
 
176
        except Exception as error:
 
177
            print(error)
 
178
 
 
179
 
 
180
class Scope (Unity.AbstractScope):
 
181
    def __init__(self):
 
182
        Unity.AbstractScope.__init__(self)
 
183
 
 
184
    def do_get_search_hint(self):
 
185
        return SEARCH_HINT
 
186
 
 
187
    def do_get_schema(self):
 
188
        '''
 
189
        Adds specific metadata fields
 
190
        '''
 
191
        schema = Unity.Schema.new()
 
192
        if EXTRA_METADATA:
 
193
            for m in EXTRA_METADATA:
 
194
                schema.add_field(m['id'], m['type'], m['field'])
 
195
        #FIXME should be REQUIRED for credits
 
196
        schema.add_field('provider_credits',
 
197
                         's',
 
198
                         Unity.SchemaFieldType.OPTIONAL)
 
199
        return schema
 
200
 
 
201
    def do_get_categories(self):
 
202
        '''
 
203
        Adds categories
 
204
        '''
 
205
        cs = Unity.CategorySet.new()
 
206
        if CATEGORIES:
 
207
            for c in CATEGORIES:
 
208
                cat = Unity.Category.new(c['id'], c['name'],
 
209
                                         Gio.ThemedIcon.new(c['icon']),
 
210
                                         c['renderer'])
 
211
                cs.add(cat)
 
212
        return cs
 
213
 
 
214
    def do_get_filters(self):
 
215
        '''
 
216
        Adds filters
 
217
        '''
 
218
        fs = Unity.FilterSet.new()
 
219
#        FIXME if FILTERS:
 
220
        return fs
 
221
 
 
222
    def do_get_group_name(self):
 
223
        return GROUP_NAME
 
224
 
 
225
    def do_get_unique_name(self):
 
226
        return UNIQUE_PATH
 
227
 
 
228
    def do_create_search_for_query(self, search_context):
 
229
        se = MySearch(search_context)
 
230
        return se
 
231
 
 
232
    def do_create_previewer(self, result, metadata):
 
233
        rp = Preview()
 
234
        rp.set_scope_result(result)
 
235
        rp.set_search_metadata(metadata)
 
236
        return rp
 
237
 
 
238
    def do_activate(self, result, metadata, id):
 
239
        return Unity.ActivationResponse()
 
240
 
 
241
 
 
242
def load_scope():
 
243
    return Scope()