Merge lp:~submarine/unity-scope-clementine/clementine-previews into lp:unity-scope-clementine
- clementine-previews
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | David Callé |
Approved revision: | 32 |
Merged at revision: | 23 |
Proposed branch: | lp:~submarine/unity-scope-clementine/clementine-previews |
Merge into: | lp:unity-scope-clementine |
Diff against target: |
513 lines (+157/-233) 3 files modified
data/clementine.scope.in (+1/-1) src/unity_clementine_daemon.py (+144/-220) tests/test_clementine.py (+12/-12) |
To merge this branch: | bzr merge lp:~submarine/unity-scope-clementine/clementine-previews |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
David Callé | Approve | ||
Review via email:
|
Commit message
Add Previews and Activation.
Description of the change
Add Previews and Activation.
Now using SQL queries to do the searching rather than using SQL to grab everything and then searching within the scope
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
FAILURE: http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:32
http://
Executed test runs:
SUCCESS: http://
Click here to trigger a rebuild:
http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
http://
Executed test runs:
SUCCESS: http://
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
David Callé (davidc3) wrote : | # |
Jenkins hiccup. Approved again.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
1 | === modified file 'data/clementine.scope.in' | |||
2 | --- data/clementine.scope.in 2013-03-20 16:43:38 +0000 | |||
3 | +++ data/clementine.scope.in 2013-04-22 13:02:11 +0000 | |||
4 | @@ -5,7 +5,7 @@ | |||
5 | 5 | QueryBinary=clementine | 5 | QueryBinary=clementine |
6 | 6 | _Keywords=clementine; | 6 | _Keywords=clementine; |
7 | 7 | RequiredMetadata= | 7 | RequiredMetadata= |
9 | 8 | OptionalMetadata= | 8 | OptionalMetadata=album[s];artist[s];genre[s];year[i];track_length[i];track_number[i] |
10 | 9 | Loader=/usr/share/unity-scopes/clementine/unity_clementine_daemon.py | 9 | Loader=/usr/share/unity-scopes/clementine/unity_clementine_daemon.py |
11 | 10 | RemoteContent=false | 10 | RemoteContent=false |
12 | 11 | Type=music | 11 | Type=music |
13 | 12 | 12 | ||
14 | === modified file 'src/unity_clementine_daemon.py' | |||
15 | --- src/unity_clementine_daemon.py 2013-03-19 15:40:44 +0000 | |||
16 | +++ src/unity_clementine_daemon.py 2013-04-22 13:02:11 +0000 | |||
17 | @@ -18,14 +18,11 @@ | |||
18 | 18 | from gi.repository import Unity | 18 | from gi.repository import Unity |
19 | 19 | import gettext | 19 | import gettext |
20 | 20 | import urllib.parse | 20 | import urllib.parse |
21 | 21 | import dbus | ||
22 | 22 | import hashlib | 21 | import hashlib |
23 | 23 | import unicodedata | 22 | import unicodedata |
24 | 24 | import os | 23 | import os |
25 | 25 | import sys | ||
26 | 26 | import shutil | 24 | import shutil |
27 | 27 | import sqlite3 | 25 | import sqlite3 |
28 | 28 | import dbus | ||
29 | 29 | 26 | ||
30 | 30 | APP_NAME = 'unity-scope-clementine' | 27 | APP_NAME = 'unity-scope-clementine' |
31 | 31 | LOCAL_PATH = '/usr/share/locale/' | 28 | LOCAL_PATH = '/usr/share/locale/' |
32 | @@ -45,7 +42,7 @@ | |||
33 | 45 | DEFAULT_RESULT_MIMETYPE = 'taglib/mp3' | 42 | DEFAULT_RESULT_MIMETYPE = 'taglib/mp3' |
34 | 46 | DEFAULT_RESULT_TYPE = Unity.ResultType.PERSONAL | 43 | DEFAULT_RESULT_TYPE = Unity.ResultType.PERSONAL |
35 | 47 | CLEMENTINE_DBFILE = os.getenv("HOME") + "/.config/Clementine/clementine.db" | 44 | CLEMENTINE_DBFILE = os.getenv("HOME") + "/.config/Clementine/clementine.db" |
37 | 48 | CLEMENTIME_BACKUP_FILE = os.getenv("HOME") + "/.config/Clementine/clementine-scope-backup.db" | 45 | CLEMENTINE_BACKUP_FILE = os.getenv("HOME") + "/.config/Clementine/clementine-scope-backup.db" |
38 | 49 | 46 | ||
39 | 50 | c1 = {'id': 'songs', | 47 | c1 = {'id': 'songs', |
40 | 51 | 'name': _('Songs'), | 48 | 'name': _('Songs'), |
41 | @@ -59,66 +56,64 @@ | |||
42 | 59 | 56 | ||
43 | 60 | FILTERS = [] | 57 | FILTERS = [] |
44 | 61 | 58 | ||
72 | 62 | m1 = {'id' :'album', | 59 | m1 = {'id': 'album', |
73 | 63 | 'type' :'s', | 60 | 'type': 's', |
74 | 64 | 'field':Unity.SchemaFieldType.OPTIONAL} | 61 | 'field': Unity.SchemaFieldType.OPTIONAL} |
75 | 65 | m2 = {'id' :'artist', | 62 | m2 = {'id': 'artist', |
76 | 66 | 'type' :'s', | 63 | 'type': 's', |
77 | 67 | 'field':Unity.SchemaFieldType.OPTIONAL} | 64 | 'field': Unity.SchemaFieldType.OPTIONAL} |
78 | 68 | m3 = {'id' :'genre', | 65 | m3 = {'id': 'genre', |
79 | 69 | 'type' :'s', | 66 | 'type': 's', |
80 | 70 | 'field':Unity.SchemaFieldType.OPTIONAL} | 67 | 'field': Unity.SchemaFieldType.OPTIONAL} |
81 | 71 | m4 = {'id' :'year', | 68 | m4 = {'id': 'year', |
82 | 72 | 'type' :'i', | 69 | 'type': 'i', |
83 | 73 | 'field':Unity.SchemaFieldType.OPTIONAL} | 70 | 'field': Unity.SchemaFieldType.OPTIONAL} |
84 | 74 | EXTRA_METADATA = [m1, m2, m3, m4] | 71 | m5 = {'id': 'track_length', |
85 | 75 | 72 | 'type': 'i', | |
86 | 76 | REFRESH_TIMEOUT = 300 | 73 | 'field': Unity.SchemaFieldType.OPTIONAL} |
87 | 77 | PREVIEW_PLAYER_DBUS_NAME = "com.canonical.Unity.Lens.Music.PreviewPlayer" | 74 | m6 = {'id': 'track_number', |
88 | 78 | PREVIEW_PLAYER_DBUS_PATH = "/com/canonical/Unity/Lens/Music/PreviewPlayer" | 75 | 'type': 'i', |
89 | 79 | PREVIEW_PLAYER_DBUS_IFACE = PREVIEW_PLAYER_DBUS_NAME | 76 | 'field': Unity.SchemaFieldType.OPTIONAL} |
90 | 80 | 77 | EXTRA_METADATA = [m1, m2, m3, m4, m5, m6] | |
91 | 81 | tracks = [] | 78 | |
92 | 82 | 79 | SEARCH_SQL = '''SELECT title, filename, artist, album, albumartist, art_automatic, year, genre, art_manual, track, length | |
93 | 83 | 80 | FROM songs | |
94 | 84 | def get_music_from_clementine(): | 81 | WHERE album LIKE '%%%s%%' OR artist LIKE '%%%s%%' OR title LIKE '%%%s%%' ORDER BY track''' |
95 | 85 | ''' | 82 | |
96 | 86 | Parses Clementine's database into a form we can use | 83 | ALBUM_SQL = '''SELECT title, filename, artist, album, albumartist, art_automatic, year, genre, art_manual, track, length |
97 | 87 | ''' | 84 | FROM songs |
98 | 88 | # Copy clementine's database to a backup so we can run searches on that rather than the main database | 85 | WHERE album LIKE '%%%s%%' AND artist LIKE '%%%s%%' ORDER BY track''' |
99 | 86 | |||
100 | 87 | |||
101 | 88 | def get_music_from_clementine(query): | ||
102 | 89 | ''' | ||
103 | 90 | Parses Clementine's database into a form we can use using the supplied SQL query | ||
104 | 91 | ''' | ||
105 | 89 | tracks = [] | 92 | tracks = [] |
107 | 90 | if not os.path.exists(CLEMENTINE_DBFILE): | 93 | if not os.path.exists(CLEMENTINE_BACKUP_FILE): |
108 | 91 | return tracks | 94 | return tracks |
109 | 92 | 95 | ||
114 | 93 | shutil.copy2(CLEMENTINE_DBFILE, CLEMENTIME_BACKUP_FILE) | 96 | conn = sqlite3.connect(CLEMENTINE_BACKUP_FILE) |
111 | 94 | |||
112 | 95 | # Grab all the data we need from the backup | ||
113 | 96 | conn = sqlite3.connect(CLEMENTIME_BACKUP_FILE) | ||
115 | 97 | cursor = conn.cursor() | 97 | cursor = conn.cursor() |
121 | 98 | # Go through the safe and grab track names, their uris, the artist name, the album title and the track's mimetypes | 98 | cursor.execute(query) |
117 | 99 | # We'll have to call them all mp3s as the mimetype isn't explicitly in the database, but as long as it's an audio mimetype, it shouldn't matter | ||
118 | 100 | cursor.execute('''SELECT title, filename, artist, album, "taglib/mp3", albumartist, art_automatic, year, genre, art_manual, track, length | ||
119 | 101 | FROM songs | ||
120 | 102 | ORDER BY track''') | ||
122 | 103 | tracks = cursor.fetchall() | 99 | tracks = cursor.fetchall() |
123 | 104 | cursor.close() | 100 | cursor.close() |
124 | 105 | print("Updated tracks from Clementine database") | ||
125 | 106 | return tracks | 101 | return tracks |
126 | 107 | 102 | ||
127 | 108 | 103 | ||
128 | 109 | def get_album_art(track): | 104 | def get_album_art(track): |
129 | 110 | # First try manually set album art | 105 | # First try manually set album art |
133 | 111 | if not track[9] is None: | 106 | if not track[8] is None: |
134 | 112 | if not track[9] == "(embedded)": | 107 | if not track[8] == "(embedded)": |
135 | 113 | return track[9] | 108 | return track[8] |
136 | 114 | 109 | ||
137 | 115 | # Next try automatically set album art | 110 | # Next try automatically set album art |
141 | 116 | if not track[6] is None: | 111 | if not track[5] is None: |
142 | 117 | if not track[6] == "(embedded)": | 112 | if not track[5] == "(embedded)": |
143 | 118 | return track[6] | 113 | return track[5] |
144 | 119 | 114 | ||
145 | 120 | # Try thumbnailing any embedded album art and use that | 115 | # Try thumbnailing any embedded album art and use that |
147 | 121 | hashname = '%s\t%s' % (unicodedata.normalize("NFKD", track[5]), unicodedata.normalize("NFKD", track[3])) | 116 | hashname = '%s\t%s' % (unicodedata.normalize("NFKD", track[4]), unicodedata.normalize("NFKD", track[3])) |
148 | 122 | file_hash = hashlib.md5(hashname.encode('utf-8')).hexdigest() | 117 | file_hash = hashlib.md5(hashname.encode('utf-8')).hexdigest() |
149 | 123 | tb_filename = os.path.join(os.path.expanduser("~/.cache/media-art"), ("album-" + file_hash)) + ".jpg" | 118 | tb_filename = os.path.join(os.path.expanduser("~/.cache/media-art"), ("album-" + file_hash)) + ".jpg" |
150 | 124 | if os.path.exists(tb_filename): | 119 | if os.path.exists(tb_filename): |
151 | @@ -149,171 +144,81 @@ | |||
152 | 149 | Search for help documents matching the search string | 144 | Search for help documents matching the search string |
153 | 150 | ''' | 145 | ''' |
154 | 151 | results = [] | 146 | results = [] |
157 | 152 | tracks = get_music_from_clementine() | 147 | shutil.copy2(CLEMENTINE_DBFILE, CLEMENTINE_BACKUP_FILE) |
158 | 153 | trackresults = [] | 148 | tracks = get_music_from_clementine(SEARCH_SQL % (search, search, search)) |
159 | 154 | albumresults = [] | 149 | albumresults = [] |
160 | 155 | 150 | ||
161 | 156 | for track in tracks: | 151 | for track in tracks: |
199 | 157 | title = u"" if track[0] is None else track[0] | 152 | title = "" if track[0] is None else track[0] |
200 | 158 | uri = u"" if track[1] is None else track[1] | 153 | uri = "" if track[1] is None else track[1].decode('utf-8') |
201 | 159 | artist = u"" if track[2] is None else track[2] | 154 | artist = "" if track[2] is None else track[2] |
202 | 160 | album = u"" if track[3] is None else track[3] | 155 | album = "" if track[3] is None else track[3] |
203 | 161 | mimetype = u"" if track[4] is None else track[4] | 156 | albumartist = "" if track[4] is None else track[4] |
204 | 162 | albumartist = u"" if track[5] is None else track[5] | 157 | year = 0 if track[6] is None else track[6] |
205 | 163 | year = 0 if track[7] is None else track[7] | 158 | genre = "" if track[7] is None else track[7] |
206 | 164 | genre = u"" if track[8] is None else track[8] | 159 | track_length = 0 if track[10] is None else track[10] / 1000000000 |
207 | 165 | trackname = title + u" - " + album + u" - " + artist | 160 | track_number = 0 if track[9] is None else track[9] |
208 | 166 | if search.lower() in trackname.lower(): | 161 | |
209 | 167 | albumart = get_album_art(track) | 162 | albumart = get_album_art(track) |
210 | 168 | albumuri = "album://" + albumartist + "/" + album | 163 | albumuri = "album://" + albumartist + "/" + album |
211 | 169 | if track not in trackresults: | 164 | results.append({'uri': urllib.parse.unquote(uri), |
212 | 170 | results.append({'uri': uri.decode('utf-8'), | 165 | 'icon': albumart, |
213 | 171 | 'icon': albumart, | 166 | 'category': 0, |
214 | 172 | 'category': 0, | 167 | 'title': title, |
215 | 173 | 'mimetype': mimetype, | 168 | 'album': GLib.Variant('s', album), |
216 | 174 | 'title': title, | 169 | 'artist': GLib.Variant('s', artist), |
217 | 175 | 'comment': artist, | 170 | 'genre': GLib.Variant('s', genre), |
218 | 176 | 'album':GLib.Variant('s', album), | 171 | 'year': GLib.Variant('i', year), |
219 | 177 | 'artist':GLib.Variant('s', artist), | 172 | 'track_length': GLib.Variant('i', track_length), |
220 | 178 | 'genre':GLib.Variant('s', genre), | 173 | 'track_number': GLib.Variant('i', track_number)}) |
221 | 179 | 'year':GLib.Variant('i', year)}) | 174 | |
222 | 180 | trackresults.append(track) | 175 | if album not in albumresults: |
223 | 181 | 176 | results.append({'uri': albumuri, | |
224 | 182 | if album not in albumresults: | 177 | 'icon': albumart, |
225 | 183 | results.append({'uri': albumuri, | 178 | 'category': 1, |
226 | 184 | 'icon': albumart, | 179 | 'title': album, |
227 | 185 | 'category': 1, | 180 | 'album': GLib.Variant('s', album), |
228 | 186 | 'mimetype': mimetype, | 181 | 'artist': GLib.Variant('s', artist), |
229 | 187 | 'title': album, | 182 | 'genre': GLib.Variant('s', genre), |
230 | 188 | 'comment': artist, | 183 | 'year': GLib.Variant('i', year)}) |
231 | 189 | 'album':GLib.Variant('s', album), | 184 | albumresults.append(album) |
195 | 190 | 'artist':GLib.Variant('s', artist), | ||
196 | 191 | 'genre':GLib.Variant('s', genre), | ||
197 | 192 | 'year':GLib.Variant('i', year)}) | ||
198 | 193 | albumresults.append(album) | ||
232 | 194 | return results | 185 | return results |
233 | 195 | 186 | ||
234 | 196 | 187 | ||
317 | 197 | def activate(scope, uri): | 188 | class Preview(Unity.ResultPreviewer): |
318 | 198 | import subprocess | 189 | |
319 | 199 | albumtracks = [] | 190 | def do_run(self): |
320 | 200 | albumtracks.append("clementine") | 191 | album = self.result.metadata['album'].get_string() |
321 | 201 | albumtracks.append("-a") | 192 | artist = self.result.metadata['artist'].get_string() |
322 | 202 | # If uri starts with album:// then we need to play all the songs on it | 193 | preview = Unity.MusicPreview.new(self.result.title, '', None) |
323 | 203 | if uri.startswith("album://"): | 194 | preview.props.image_source_uri = 'file://%s' % self.result.icon_hint |
324 | 204 | for track in tracks: | 195 | preview.props.subtitle = self.result.metadata['artist'].get_string() |
325 | 205 | album = "album://" + track[5] + "/" + track[3] | 196 | if self.result.uri.startswith("album://"): |
326 | 206 | if not album.find(uri) == -1: | 197 | tracks = get_music_from_clementine(ALBUM_SQL % (album, artist)) |
327 | 207 | albumtrack = urllib.parse.unquote(str(track[1])) | 198 | for track in tracks: |
328 | 208 | albumtracks.append(albumtrack) | 199 | track = Unity.TrackMetadata.full(track[1].decode('utf-8'), |
329 | 209 | subprocess.Popen(albumtracks) | 200 | track[9], |
330 | 210 | else: | 201 | track[0], |
331 | 211 | albumtracks.append(uri) | 202 | track[2], |
332 | 212 | subprocess.Popen(albumtracks) | 203 | track[3], |
333 | 213 | return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='') | 204 | track[10] / 1000000000) |
252 | 214 | |||
253 | 215 | |||
254 | 216 | def show_in_folder(scope, uri): | ||
255 | 217 | """ Shows the folder containing the selected track as requested from the Preview | ||
256 | 218 | """ | ||
257 | 219 | if uri.startswith("album://"): | ||
258 | 220 | for track in tracks: | ||
259 | 221 | album = "album://" + track[2] + "/" + track[3] | ||
260 | 222 | if not album.find(uri) == -1: | ||
261 | 223 | filename = track[1] | ||
262 | 224 | continue | ||
263 | 225 | else: | ||
264 | 226 | filename = uri | ||
265 | 227 | dirname = os.path.dirname(filename) | ||
266 | 228 | dirname = dirname.replace("%20", "\ ") | ||
267 | 229 | os.system("xdg-open '%s'" % str(dirname)) | ||
268 | 230 | return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='') | ||
269 | 231 | |||
270 | 232 | |||
271 | 233 | def preview_uri(scope, uri): | ||
272 | 234 | """Preview request handler""" | ||
273 | 235 | albumtracks = [] | ||
274 | 236 | isalbum = False | ||
275 | 237 | if uri.startswith("album://"): | ||
276 | 238 | isalbum = True | ||
277 | 239 | for track in tracks: | ||
278 | 240 | album = "album://" + track[2] + "/" + track[3] | ||
279 | 241 | if not album.find(uri) == -1: | ||
280 | 242 | albumtracks.append(track) | ||
281 | 243 | albumtracks.sort(key=lambda track: int(track[7])) | ||
282 | 244 | else: | ||
283 | 245 | for track in tracks: | ||
284 | 246 | album = "file://" + track[1] | ||
285 | 247 | if not album.find(uri) == -1: | ||
286 | 248 | albumtracks.append(track) | ||
287 | 249 | iteration = model.get_first_iter() | ||
288 | 250 | end_iter = model.get_last_iter() | ||
289 | 251 | while iteration != end_iter: | ||
290 | 252 | if model.get_value(iteration, 0) == uri: | ||
291 | 253 | title = model.get_value(iteration, 5) | ||
292 | 254 | description = model.get_value(iteration, 6) | ||
293 | 255 | if model.get_value(iteration, 1) == "musique": | ||
294 | 256 | image = "file:///usr/share/icons/hicolor/scalable/apps/audacious.svg" | ||
295 | 257 | else: | ||
296 | 258 | image = "file://%s" % model.get_value(iteration, 1) | ||
297 | 259 | |||
298 | 260 | preview = Unity.MusicPreview.new(title, description, None) | ||
299 | 261 | preview.props.image_source_uri = image | ||
300 | 262 | for albumtrack in albumtracks: | ||
301 | 263 | if isalbum: | ||
302 | 264 | track = Unity.TrackMetadata.full("file://" + urllib.parse.unquote(str(albumtrack[1])), # uri | ||
303 | 265 | int(albumtrack[7]), # track number | ||
304 | 266 | albumtrack[0], # track title | ||
305 | 267 | albumtrack[2], # artist | ||
306 | 268 | albumtrack[3], # album | ||
307 | 269 | int(albumtrack[8]) / 1000) # track length | ||
308 | 270 | else: | ||
309 | 271 | preview = Unity.MusicPreview.new(albumtrack[0], "", None) | ||
310 | 272 | preview.props.image_source_uri = image | ||
311 | 273 | track = Unity.TrackMetadata.full("file://" + urllib.parse.unquote(str(albumtrack[1])), | ||
312 | 274 | int(albumtrack[7]), | ||
313 | 275 | albumtrack[0], | ||
314 | 276 | albumtrack[2], | ||
315 | 277 | albumtrack[3], | ||
316 | 278 | int(albumtrack[8]) / 1000) | ||
334 | 279 | preview.add_track(track) | 205 | preview.add_track(track) |
372 | 280 | 206 | else: | |
373 | 281 | # Add the "Play" action | 207 | track = Unity.TrackMetadata.full(self.result.uri, |
374 | 282 | play_action = Unity.PreviewAction.new("activate_uri", "Play", None) | 208 | self.result.metadata['track_number'].get_int32(), |
375 | 283 | play_action.connect("activated", activate) | 209 | self.result.title, |
376 | 284 | preview.add_action(play_action) | 210 | self.result.metadata['artist'].get_string(), |
377 | 285 | 211 | self.result.metadata['album'].get_string(), | |
378 | 286 | # Add the "Show in folder" action | 212 | self.result.metadata['track_length'].get_int32()) |
379 | 287 | show_action = Unity.PreviewAction.new("show_in_folder", "Show In Folder", None) | 213 | preview.add_track(track) |
380 | 288 | show_action.connect("activated", show_in_folder) | 214 | |
381 | 289 | preview.add_action(show_action) | 215 | icon = Gio.FileIcon.new(Gio.file_new_for_path(PROVIDER_ICON)) |
382 | 290 | 216 | view_action = Unity.PreviewAction.new("play", _("Play"), None) | |
383 | 291 | preview.connect("play", play) | 217 | preview.add_action(view_action) |
384 | 292 | preview.connect("pause", pause) | 218 | show_action = Unity.PreviewAction.new("show", _("Show in Folder"), None) |
385 | 293 | preview.connect("closed", closed) | 219 | preview.add_action(show_action) |
386 | 294 | break | 220 | return preview |
387 | 295 | iteration = model.next(iteration) | 221 | |
351 | 296 | if preview is None: | ||
352 | 297 | print("Couldn't find model row for requested preview uri: '%s'", uri) | ||
353 | 298 | return preview | ||
354 | 299 | |||
355 | 300 | |||
356 | 301 | def play(preview, uri): | ||
357 | 302 | """Plays the selected track as selected in the Preview""" | ||
358 | 303 | player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) | ||
359 | 304 | dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Play(uri) | ||
360 | 305 | |||
361 | 306 | |||
362 | 307 | def pause(preview, uri): | ||
363 | 308 | """Pauses the selected track as selected in the Preview""" | ||
364 | 309 | player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) | ||
365 | 310 | dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Pause() | ||
366 | 311 | |||
367 | 312 | |||
368 | 313 | def closed(preview): | ||
369 | 314 | """Stops playing when the previre is closed""" | ||
370 | 315 | player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) | ||
371 | 316 | dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Close() | ||
388 | 317 | 222 | ||
389 | 318 | # Classes below this point establish communication | 223 | # Classes below this point establish communication |
390 | 319 | # with Unity, you probably shouldn't modify them. | 224 | # with Unity, you probably shouldn't modify them. |
391 | @@ -348,19 +253,8 @@ | |||
392 | 348 | i['comment'] = '' | 253 | i['comment'] = '' |
393 | 349 | if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '': | 254 | if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '': |
394 | 350 | i['dnd_uri'] = i['uri'] | 255 | i['dnd_uri'] = i['uri'] |
408 | 351 | i['metadata'] = {} | 256 | i['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS) |
409 | 352 | if EXTRA_METADATA: | 257 | result_set.add_result(**i) |
397 | 353 | for e in i: | ||
398 | 354 | for m in EXTRA_METADATA: | ||
399 | 355 | if m['id'] == e: | ||
400 | 356 | i['metadata'][e] = i[e] | ||
401 | 357 | i['metadata']['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS) | ||
402 | 358 | result = Unity.ScopeResult.create(str(i['uri']), str(i['icon']), | ||
403 | 359 | i['category'], i['result_type'], | ||
404 | 360 | str(i['mimetype']), str(i['title']), | ||
405 | 361 | str(i['comment']), str(i['dnd_uri']), | ||
406 | 362 | i['metadata']) | ||
407 | 363 | result_set.add_result(result) | ||
410 | 364 | except Exception as error: | 258 | except Exception as error: |
411 | 365 | print(error) | 259 | print(error) |
412 | 366 | 260 | ||
413 | @@ -416,6 +310,36 @@ | |||
414 | 416 | se = MySearch(search_context) | 310 | se = MySearch(search_context) |
415 | 417 | return se | 311 | return se |
416 | 418 | 312 | ||
417 | 313 | def do_activate(self, result, metadata, id): | ||
418 | 314 | album = result.metadata['album'].get_string() | ||
419 | 315 | artist = result.metadata['artist'].get_string() | ||
420 | 316 | |||
421 | 317 | if id == 'show': | ||
422 | 318 | if result.uri.startswith("album://"): | ||
423 | 319 | tracks = get_music_from_clementine(ALBUM_SQL % (album, artist)) | ||
424 | 320 | filename = tracks[0][1].decode('utf-8') | ||
425 | 321 | else: | ||
426 | 322 | filename = result.uri | ||
427 | 323 | dirname = os.path.dirname(filename) | ||
428 | 324 | os.system("xdg-open '%s'" % str(dirname)) | ||
429 | 325 | else: | ||
430 | 326 | albumtracks = '' | ||
431 | 327 | if result.uri.startswith('album://'): | ||
432 | 328 | tracks = get_music_from_clementine(ALBUM_SQL % (album, artist)) | ||
433 | 329 | for track in tracks: | ||
434 | 330 | albumtracks = '%s \'%s\'' % (albumtracks, track[1].decode('utf-8')) | ||
435 | 331 | else: | ||
436 | 332 | albumtracks = result.uri | ||
437 | 333 | os.system('clementine -a %s' % albumtracks) | ||
438 | 334 | |||
439 | 335 | return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri=None) | ||
440 | 336 | |||
441 | 337 | def do_create_previewer(self, result, metadata): | ||
442 | 338 | rp = Preview() | ||
443 | 339 | rp.set_scope_result(result) | ||
444 | 340 | rp.set_search_metadata(metadata) | ||
445 | 341 | return rp | ||
446 | 342 | |||
447 | 419 | 343 | ||
448 | 420 | def load_scope(): | 344 | def load_scope(): |
449 | 421 | return Scope() | 345 | return Scope() |
450 | 422 | 346 | ||
451 | === modified file 'tests/test_clementine.py' | |||
452 | --- tests/test_clementine.py 2013-02-18 15:48:48 +0000 | |||
453 | +++ tests/test_clementine.py 2013-04-22 13:02:11 +0000 | |||
454 | @@ -11,17 +11,18 @@ | |||
455 | 11 | self.results = [] | 11 | self.results = [] |
456 | 12 | 12 | ||
457 | 13 | def do_add_result(self, result): | 13 | def do_add_result(self, result): |
462 | 14 | self.results.append({'uri':result.uri, | 14 | self.results.append({'uri': result.uri, |
463 | 15 | 'title':result.title, | 15 | 'title': result.title, |
464 | 16 | 'comment':result.comment, | 16 | 'comment': result.comment, |
465 | 17 | 'icon':result.icon_hint}) | 17 | 'icon': result.icon_hint}) |
466 | 18 | |||
467 | 18 | 19 | ||
468 | 19 | class ScopeTestCase(TestCase): | 20 | class ScopeTestCase(TestCase): |
469 | 20 | def init_scope(self, scope_path): | 21 | def init_scope(self, scope_path): |
470 | 21 | self.scope_module = imp.load_source('scope', scope_path) | 22 | self.scope_module = imp.load_source('scope', scope_path) |
471 | 22 | self.scope = self.scope_module.load_scope() | 23 | self.scope = self.scope_module.load_scope() |
472 | 23 | 24 | ||
474 | 24 | def perform_query(self, query, filter_set = Unity.FilterSet.new()): | 25 | def perform_query(self, query, filter_set=Unity.FilterSet.new()): |
475 | 25 | result_set = ResultSet() | 26 | result_set = ResultSet() |
476 | 26 | ctx = Unity.SearchContext.create(query, 0, filter_set, | 27 | ctx = Unity.SearchContext.create(query, 0, filter_set, |
477 | 27 | None, result_set, None) | 28 | None, result_set, None) |
478 | @@ -30,7 +31,7 @@ | |||
479 | 30 | return result_set | 31 | return result_set |
480 | 31 | 32 | ||
481 | 32 | 33 | ||
483 | 33 | class TestAskUbuntu(ScopeTestCase): | 34 | class TestClementine(ScopeTestCase): |
484 | 34 | def setUp(self): | 35 | def setUp(self): |
485 | 35 | self.init_scope('src/unity_clementine_daemon.py') | 36 | self.init_scope('src/unity_clementine_daemon.py') |
486 | 36 | 37 | ||
487 | @@ -38,10 +39,10 @@ | |||
488 | 38 | self.scope = None | 39 | self.scope = None |
489 | 39 | self.scope_module = None | 40 | self.scope_module = None |
490 | 40 | 41 | ||
492 | 41 | def test_questions_search(self): | 42 | def test_search(self): |
493 | 42 | self.scope_module.CLEMENTINE_DBFILE = 'tests/data/mock_clementine_pass.db' | 43 | self.scope_module.CLEMENTINE_DBFILE = 'tests/data/mock_clementine_pass.db' |
496 | 43 | self.scope_module.CLEMENTIME_BACKUP_FILE = 'tests/data/mock_clementine_backup.db' | 44 | self.scope_module.CLEMENTINE_BACKUP_FILE = 'tests/data/mock_clementine_backup.db' |
497 | 44 | expected_results = ["file:///home/mark/Music/Bell%20X1/Flock/01.%20Reacharound.mp3", | 45 | expected_results = ["file:///home/mark/Music/Bell X1/Flock/01. Reacharound.mp3", |
498 | 45 | "Reacharound", | 46 | "Reacharound", |
499 | 46 | "album://Bell X1/Flock", | 47 | "album://Bell X1/Flock", |
500 | 47 | "Flock"] | 48 | "Flock"] |
501 | @@ -54,10 +55,9 @@ | |||
502 | 54 | results.append(result_set.results[1]['title']) | 55 | results.append(result_set.results[1]['title']) |
503 | 55 | self.assertEqual(results, expected_results) | 56 | self.assertEqual(results, expected_results) |
504 | 56 | 57 | ||
507 | 57 | 58 | def test_failing_search(self): | |
506 | 58 | def test_questions_failing_search(self): | ||
508 | 59 | self.scope_module.CLEMENTINE_DBFILE = 'tests/data/mock_clementine_fail' | 59 | self.scope_module.CLEMENTINE_DBFILE = 'tests/data/mock_clementine_fail' |
510 | 60 | self.scope_module.CLEMENTIME_BACKUP_FILE = 'tests/data/mock_clementine_backup.db' | 60 | self.scope_module.CLEMENTINE_BACKUP_FILE = 'tests/data/mock_clementine_backup.db' |
511 | 61 | for s in ['upnriitnyt']: | 61 | for s in ['upnriitnyt']: |
512 | 62 | result_set = self.perform_query(s) | 62 | result_set = self.perform_query(s) |
513 | 63 | self.assertEqual(len(result_set.results), 0) | 63 | self.assertEqual(len(result_set.results), 0) |
FAILED: Continuous integration, rev:31 jenkins. qa.ubuntu. com/job/ unity-scope- clementine- ci/1/ jenkins. qa.ubuntu. com/job/ unity-scope- clementine- raring- amd64-ci/ 1/console
http://
Executed test runs:
FAILURE: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ unity-scope- clementine- ci/1/rebuild
http://