Merge lp:~submarine/unity-scope-gmusicbrowser/gmusicbrowser-previews into lp:unity-scope-gmusicbrowser
- gmusicbrowser-previews
- Merge into trunk
Proposed by
Mark Tully
Status: | Merged |
---|---|
Approved by: | David Callé |
Approved revision: | 21 |
Merged at revision: | 20 |
Proposed branch: | lp:~submarine/unity-scope-gmusicbrowser/gmusicbrowser-previews |
Merge into: | lp:unity-scope-gmusicbrowser |
Diff against target: |
411 lines (+104/-167) 3 files modified
data/gmusicbrowser.scope.in (+3/-1) src/unity_gmusicbrowser_daemon.py (+92/-157) tests/test_gmusicbrowser.py (+9/-9) |
To merge this branch: | bzr merge lp:~submarine/unity-scope-gmusicbrowser/gmusicbrowser-previews |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
David Callé | Approve | ||
Review via email: mp+159935@code.launchpad.net |
Commit message
Add Previews and Activation
Description of the change
Add Previews and Activation
To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : | # |
review:
Needs Fixing
(continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
review:
Needs Fixing
(continuous-integration)
- 21. By Mark Tully
-
Fixed tests
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:21
http://
Executed test runs:
SUCCESS: http://
Click here to trigger a rebuild:
http://
review:
Approve
(continuous-integration)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'data/gmusicbrowser.scope.in' |
2 | --- data/gmusicbrowser.scope.in 2013-03-20 16:43:38 +0000 |
3 | +++ data/gmusicbrowser.scope.in 2013-04-22 13:23:30 +0000 |
4 | @@ -5,7 +5,9 @@ |
5 | QueryBinary=gmusicbrowser |
6 | _Keywords=gmusicbrowser; |
7 | RequiredMetadata= |
8 | -OptionalMetadata= |
9 | +OptionalMetadata=album[s];artist[s];genre[s];year[i];track_length[i];track_number[i] |
10 | +Loader=/usr/share/unity-scopes/gmusicbrowser/unity_gmusicbrowser_daemon.py |
11 | +RemoteContent=false |
12 | Type=music |
13 | _Name=Gmusicbrowser |
14 | _Description=Find Gmusicbrowser items |
15 | |
16 | === modified file 'src/unity_gmusicbrowser_daemon.py' |
17 | --- src/unity_gmusicbrowser_daemon.py 2013-03-19 15:40:16 +0000 |
18 | +++ src/unity_gmusicbrowser_daemon.py 2013-04-22 13:23:30 +0000 |
19 | @@ -64,12 +64,13 @@ |
20 | m4 = {'id' :'year', |
21 | 'type' :'i', |
22 | 'field':Unity.SchemaFieldType.OPTIONAL} |
23 | -EXTRA_METADATA = [m1, m2, m3, m4] |
24 | - |
25 | -REFRESH_TIMEOUT = 300 |
26 | -PREVIEW_PLAYER_DBUS_NAME = "com.canonical.Unity.Lens.Music.PreviewPlayer" |
27 | -PREVIEW_PLAYER_DBUS_PATH = "/com/canonical/Unity/Lens/Music/PreviewPlayer" |
28 | -PREVIEW_PLAYER_DBUS_IFACE = PREVIEW_PLAYER_DBUS_NAME |
29 | +m5 = {'id': 'track_length', |
30 | + 'type': 'i', |
31 | + 'field': Unity.SchemaFieldType.OPTIONAL} |
32 | +m6 = {'id': 'track_number', |
33 | + 'type': 'i', |
34 | + 'field': Unity.SchemaFieldType.OPTIONAL} |
35 | +EXTRA_METADATA = [m1, m2, m3, m4, m5, m6] |
36 | |
37 | tracks = [] |
38 | albumart = [] |
39 | @@ -77,6 +78,8 @@ |
40 | |
41 | def get_music_from_gmusicbrowser(): |
42 | """Parses GMusicBrowser's collection into a form we can use""" |
43 | + global tracks |
44 | + global albumart |
45 | tracks = [] |
46 | albumart = [] |
47 | songlist = [] |
48 | @@ -95,7 +98,6 @@ |
49 | albumstart = index |
50 | if item == '[artist]\n': |
51 | artiststart = index |
52 | - print(songstart) |
53 | except: |
54 | return tracks |
55 | |
56 | @@ -122,7 +124,7 @@ |
57 | if track[7] == "": |
58 | track[7] = "0" |
59 | track[8] = 0 if fields[32] is None else fields[32] |
60 | - track[9] = fields[32] |
61 | + track[9] = int(fields[32]) |
62 | track[10] = fields[17] |
63 | tracks.append(track) |
64 | albumartlist = database[albumstart + 2:artiststart - 1] |
65 | @@ -135,7 +137,8 @@ |
66 | albumarta[2] = fields[1] |
67 | albumart.append(albumarta) |
68 | print("Updated tracks from GMusicBrowser database") |
69 | - |
70 | + |
71 | + tracks.sort(key=lambda track: track[9]) |
72 | return albumart, tracks |
73 | |
74 | |
75 | @@ -149,6 +152,8 @@ |
76 | ''' |
77 | Search for help documents matching the search string |
78 | ''' |
79 | + global tracks |
80 | + global albumart |
81 | results = [] |
82 | albums, tracks = get_music_from_gmusicbrowser() |
83 | trackresults = [] |
84 | @@ -162,166 +167,75 @@ |
85 | albumartist = "" if track[5] is None else track[5] |
86 | year = 0 if track[7] is None else int(track[7]) |
87 | genre = "" if track[6] is None else track[6] |
88 | + track_length = 0 if track[10] is None else int(track[10]) |
89 | + track_number = 0 if track[9] is None else int(track[9]) |
90 | trackname = title + " - " + album + " - " + artist |
91 | if search.lower() in trackname.lower(): |
92 | - albumart = get_album_art(track, albums) |
93 | + albumart = 'file://' + get_album_art(track, albums).replace('\n', '') |
94 | albumuri = "album://" + albumartist + "/" + album |
95 | if track not in trackresults: |
96 | results.append({'uri': uri, |
97 | 'icon': albumart, |
98 | 'category': 0, |
99 | - 'mimetype': mimetype, |
100 | 'title': title, |
101 | - 'comment': artist, |
102 | 'album':GLib.Variant('s', album), |
103 | 'artist':GLib.Variant('s', artist), |
104 | 'genre':GLib.Variant('s', genre), |
105 | - 'year':GLib.Variant('i', year)}) |
106 | + 'year':GLib.Variant('i', year), |
107 | + 'track_length': GLib.Variant('i', track_length), |
108 | + 'track_number': GLib.Variant('i', track_number)}) |
109 | trackresults.append(track) |
110 | |
111 | if album not in albumresults: |
112 | results.append({'uri': albumuri, |
113 | 'icon': albumart, |
114 | 'category': 1, |
115 | - 'mimetype': mimetype, |
116 | 'title': album, |
117 | - 'comment': artist, |
118 | 'album':GLib.Variant('s', album), |
119 | 'artist':GLib.Variant('s', artist), |
120 | 'genre':GLib.Variant('s', genre), |
121 | - 'year':GLib.Variant('i', year)}) |
122 | + 'year':GLib.Variant('i', year), |
123 | + 'track_length': GLib.Variant('i', track_length), |
124 | + 'track_number': GLib.Variant('i', track_number)}) |
125 | albumresults.append(album) |
126 | return results |
127 | |
128 | |
129 | -def activate(scope, uri): |
130 | - import subprocess |
131 | - albumtracks = [] |
132 | - albumtracks.append("gmusicbrowser") |
133 | - albumtracks.append("-play") |
134 | - albumtracks.append("-playlist") |
135 | - # If uri starts with album:// then we need to play all the songs on it |
136 | - if uri.startswith("album://"): |
137 | - trackdetails = [] |
138 | - for track in tracks: |
139 | - trackdetail = [] |
140 | - album = "album://" + track[5] + "/" + track[3] |
141 | - if not album.find(uri) == -1: |
142 | - albumtrack = track[1] |
143 | - trackdetail.append(albumtrack) |
144 | - trackdetail.append(track[8]) |
145 | - trackdetails.append(trackdetail) |
146 | - trackdetails.sort(key=lambda track: int(track[1])) |
147 | - for track in trackdetails: |
148 | - albumtracks.append(track[0]) |
149 | - subprocess.Popen(albumtracks) |
150 | - else: |
151 | - albumtracks.append("file://" + uri) |
152 | - subprocess.Popen(albumtracks) |
153 | - return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='') |
154 | - |
155 | - |
156 | -def show_in_folder(scope, uri): |
157 | - """ Shows the folder containing the selected track as requested from the Preview |
158 | - """ |
159 | - if uri.startswith("album://"): |
160 | - for track in tracks: |
161 | - album = "album://" + track[2] + "/" + track[3] |
162 | - if not album.find(uri) == -1: |
163 | - filename = track[1] |
164 | - continue |
165 | - else: |
166 | - filename = uri |
167 | - dirname = os.path.dirname(filename) |
168 | - dirname = dirname.replace("%20", "\ ") |
169 | - os.system("xdg-open '%s'" % str(dirname)) |
170 | - return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='') |
171 | - |
172 | - |
173 | -def preview_uri(scope, uri): |
174 | - """Preview request handler""" |
175 | - albumtracks = [] |
176 | - isalbum = False |
177 | - if uri.startswith("album://"): |
178 | - isalbum = True |
179 | - for track in tracks: |
180 | - album = "album://" + track[2] + "/" + track[3] |
181 | - if not album.find(uri) == -1: |
182 | - albumtracks.append(track) |
183 | - albumtracks.sort(key=lambda track: int(track[7])) |
184 | - else: |
185 | - for track in tracks: |
186 | - album = "file://" + track[1] |
187 | - if not album.find(uri) == -1: |
188 | - albumtracks.append(track) |
189 | - iteration = model.get_first_iter() |
190 | - end_iter = model.get_last_iter() |
191 | - while iteration != end_iter: |
192 | - if model.get_value(iteration, 0) == uri: |
193 | - title = model.get_value(iteration, 5) |
194 | - description = model.get_value(iteration, 6) |
195 | - if model.get_value(iteration, 1) == "musique": |
196 | - image = "file:///usr/share/icons/hicolor/scalable/apps/audacious.svg" |
197 | - else: |
198 | - image = "file://%s" % model.get_value(iteration, 1) |
199 | - |
200 | - preview = Unity.MusicPreview.new(title, description, None) |
201 | - preview.props.image_source_uri = image |
202 | - for albumtrack in albumtracks: |
203 | - if isalbum: |
204 | - track = Unity.TrackMetadata.full("file://" + urllib.parse.unquote(str(albumtrack[1])), # uri |
205 | - int(albumtrack[7]), # track number |
206 | - albumtrack[0], # track title |
207 | - albumtrack[2], # artist |
208 | - albumtrack[3], # album |
209 | - int(albumtrack[8]) / 1000) # track length |
210 | - else: |
211 | - preview = Unity.MusicPreview.new(albumtrack[0], "", None) |
212 | - preview.props.image_source_uri = image |
213 | - track = Unity.TrackMetadata.full("file://" + urllib.parse.unquote(str(albumtrack[1])), |
214 | - int(albumtrack[7]), |
215 | - albumtrack[0], |
216 | - albumtrack[2], |
217 | - albumtrack[3], |
218 | - int(albumtrack[8]) / 1000) |
219 | - preview.add_track(track) |
220 | - |
221 | - # Add the "Play" action |
222 | - play_action = Unity.PreviewAction.new("activate_uri", "Play", None) |
223 | - play_action.connect("activated", activate) |
224 | - preview.add_action(play_action) |
225 | - |
226 | - # Add the "Show in folder" action |
227 | - show_action = Unity.PreviewAction.new("show_in_folder", "Show In Folder", None) |
228 | - show_action.connect("activated", show_in_folder) |
229 | - preview.add_action(show_action) |
230 | - |
231 | - preview.connect("play", play) |
232 | - preview.connect("pause", pause) |
233 | - preview.connect("closed", closed) |
234 | - break |
235 | - iteration = model.next(iteration) |
236 | - if preview is None: |
237 | - print("Couldn't find model row for requested preview uri: '%s'", uri) |
238 | - return preview |
239 | - |
240 | - |
241 | -def play(preview, uri): |
242 | - """Plays the selected track as selected in the Preview""" |
243 | - player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) |
244 | - dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Play(uri) |
245 | - |
246 | - |
247 | -def pause(preview, uri): |
248 | - """Pauses the selected track as selected in the Preview""" |
249 | - player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) |
250 | - dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Pause() |
251 | - |
252 | - |
253 | -def closed(preview): |
254 | - """Stops playing when the previre is closed""" |
255 | - player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) |
256 | - dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Close() |
257 | +class Preview(Unity.ResultPreviewer): |
258 | + |
259 | + def do_run(self): |
260 | + global tracks |
261 | + album = self.result.metadata['album'].get_string() |
262 | + artist = self.result.metadata['artist'].get_string() |
263 | + preview = Unity.MusicPreview.new(self.result.title, '', None) |
264 | + preview.props.image_source_uri = self.result.icon_hint |
265 | + preview.props.subtitle = self.result.metadata['artist'].get_string() |
266 | + if self.result.uri.startswith("album://"): |
267 | + for track in tracks: |
268 | + if album in track[3] and artist in track[2]: |
269 | + track = Unity.TrackMetadata.full(track[1], |
270 | + int(track[9]), |
271 | + track[0], |
272 | + track[2], |
273 | + track[3], |
274 | + int(track[10])) |
275 | + preview.add_track(track) |
276 | + else: |
277 | + print(self.result.uri) |
278 | + track = Unity.TrackMetadata.full('file://%s' % self.result.uri.replace(' ', '%20'), |
279 | + self.result.metadata['track_number'].get_int32(), |
280 | + self.result.title, |
281 | + self.result.metadata['artist'].get_string(), |
282 | + self.result.metadata['album'].get_string(), |
283 | + self.result.metadata['track_length'].get_int32()) |
284 | + preview.add_track(track) |
285 | + |
286 | + view_action = Unity.PreviewAction.new("play", _("Play"), None) |
287 | + preview.add_action(view_action) |
288 | + show_action = Unity.PreviewAction.new("show", _("Show in Folder"), None) |
289 | + preview.add_action(show_action) |
290 | + return preview |
291 | + |
292 | |
293 | # Classes below this point establish communication |
294 | # with Unity, you probably shouldn't modify them. |
295 | @@ -356,19 +270,8 @@ |
296 | i['comment'] = '' |
297 | if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '': |
298 | i['dnd_uri'] = i['uri'] |
299 | - i['metadata'] = {} |
300 | - if EXTRA_METADATA: |
301 | - for e in i: |
302 | - for m in EXTRA_METADATA: |
303 | - if m['id'] == e: |
304 | - i['metadata'][e] = i[e] |
305 | - i['metadata']['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS) |
306 | - result = Unity.ScopeResult.create(str(i['uri']), str(i['icon']), |
307 | - i['category'], i['result_type'], |
308 | - str(i['mimetype']), str(i['title']), |
309 | - str(i['comment']), str(i['dnd_uri']), |
310 | - i['metadata']) |
311 | - result_set.add_result(result) |
312 | + i['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS) |
313 | + result_set.add_result(**i) |
314 | except Exception as error: |
315 | print(error) |
316 | |
317 | @@ -424,6 +327,38 @@ |
318 | se = MySearch(search_context) |
319 | return se |
320 | |
321 | + def do_activate(self, result, metadata, id): |
322 | + album = result.metadata['album'].get_string() |
323 | + artist = result.metadata['artist'].get_string() |
324 | + global tracks |
325 | + if id == 'show': |
326 | + if result.uri.startswith("album://"): |
327 | + for track in tracks: |
328 | + if album in track[3] and artist in track[2]: |
329 | + filename = tracks[1] |
330 | + continue |
331 | + else: |
332 | + filename = result.uri |
333 | + dirname = os.path.dirname(filename) |
334 | + os.system("xdg-open '%s'" % str(dirname)) |
335 | + else: |
336 | + albumtracks = '' |
337 | + if result.uri.startswith('album://'): |
338 | + for track in tracks: |
339 | + if album in track[3] and artist in track[2]: |
340 | + albumtracks = albumtracks + ' \'%s\'' % (track[1]) |
341 | + else: |
342 | + albumtracks = '\'%s\'' % result.uri |
343 | + os.system('gmusicbrowser -play -playlist %s' % albumtracks) |
344 | + |
345 | + return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri=None) |
346 | + |
347 | + def do_create_previewer(self, result, metadata): |
348 | + rp = Preview() |
349 | + rp.set_scope_result(result) |
350 | + rp.set_search_metadata(metadata) |
351 | + return rp |
352 | + |
353 | |
354 | def load_scope(): |
355 | return Scope() |
356 | |
357 | === modified file 'tests/test_gmusicbrowser.py' |
358 | --- tests/test_gmusicbrowser.py 2013-03-03 19:20:19 +0000 |
359 | +++ tests/test_gmusicbrowser.py 2013-04-22 13:23:30 +0000 |
360 | @@ -11,17 +11,18 @@ |
361 | self.results = [] |
362 | |
363 | def do_add_result(self, result): |
364 | - self.results.append({'uri':result.uri, |
365 | - 'title':result.title, |
366 | - 'comment':result.comment, |
367 | - 'icon':result.icon_hint}) |
368 | + self.results.append({'uri': result.uri, |
369 | + 'title': result.title, |
370 | + 'comment': result.comment, |
371 | + 'icon': result.icon_hint}) |
372 | + |
373 | |
374 | class ScopeTestCase(TestCase): |
375 | def init_scope(self, scope_path): |
376 | self.scope_module = imp.load_source('scope', scope_path) |
377 | self.scope = self.scope_module.load_scope() |
378 | |
379 | - def perform_query(self, query, filter_set = Unity.FilterSet.new()): |
380 | + def perform_query(self, query, filter_set=Unity.FilterSet.new()): |
381 | result_set = ResultSet() |
382 | ctx = Unity.SearchContext.create(query, 0, filter_set, |
383 | None, result_set, None) |
384 | @@ -30,7 +31,7 @@ |
385 | return result_set |
386 | |
387 | |
388 | -class TestAskUbuntu(ScopeTestCase): |
389 | +class TestGmusicbrowser(ScopeTestCase): |
390 | def setUp(self): |
391 | self.init_scope('src/unity_gmusicbrowser_daemon.py') |
392 | |
393 | @@ -38,7 +39,7 @@ |
394 | self.scope = None |
395 | self.scope_module = None |
396 | |
397 | - def test_questions_search(self): |
398 | + def test_qsearch(self): |
399 | self.scope_module.GMUSICBROWSER_DBFILE = 'tests/data/mock_gmusicbrowser_pass' |
400 | expected_results = ["/home/mark/Music/Jacques Dutronc/En Vogue/02. Mini, Mini, Mini.mp3", |
401 | "Mini, Mini, Mini"] |
402 | @@ -49,8 +50,7 @@ |
403 | results.append(result_set.results[0]['title']) |
404 | self.assertEqual(results, expected_results) |
405 | |
406 | - |
407 | - def test_questions_failing_search(self): |
408 | + def test_failing_search(self): |
409 | self.scope_module.GMUSICBROWSER_DBFILE = 'tests/data/mock_gmusicbrowser_fail' |
410 | for s in ['upnriitnyt']: |
411 | result_set = self.perform_query(s) |
FAILED: Continuous integration, rev:20 jenkins. qa.ubuntu. com/job/ unity-scope- gmusicbrowser- ci/1/ jenkins. qa.ubuntu. com/job/ unity-scope- gmusicbrowser- raring- amd64-ci/ 1/console
http://
Executed test runs:
FAILURE: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ unity-scope- gmusicbrowser- ci/1/rebuild
http://