Merge lp:~submarine/unity-scope-musique/musique-previews into lp:unity-scope-musique
- musique-previews
- Merge into trunk
Proposed by
Mark Tully
Status: | Merged |
---|---|
Approved by: | David Callé |
Approved revision: | 20 |
Merged at revision: | 18 |
Proposed branch: | lp:~submarine/unity-scope-musique/musique-previews |
Merge into: | lp:unity-scope-musique |
Diff against target: |
374 lines (+117/-193) 2 files modified
data/musique.scope.in (+3/-1) src/unity_musique_daemon.py (+114/-192) |
To merge this branch: | bzr merge lp:~submarine/unity-scope-musique/musique-previews |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
David Callé | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+159940@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:
Approve
(continuous-integration)
Revision history for this message
David Callé (davidc3) wrote : | # |
Even if the activation doesn't open the player on the result, it's the best we can do with Musique in its current state. +1
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'data/musique.scope.in' |
2 | --- data/musique.scope.in 2013-03-20 16:43:39 +0000 |
3 | +++ data/musique.scope.in 2013-04-20 13:49:41 +0000 |
4 | @@ -5,7 +5,9 @@ |
5 | QueryBinary=musique |
6 | _Keywords=musique; |
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/musique/unity_musique_daemon.py |
11 | +RemoteContent=false |
12 | Type=music |
13 | _Name=Musique |
14 | _Description=Find Musique items |
15 | |
16 | === modified file 'src/unity_musique_daemon.py' |
17 | --- src/unity_musique_daemon.py 2013-03-19 15:40:31 +0000 |
18 | +++ src/unity_musique_daemon.py 2013-04-20 13:49:41 +0000 |
19 | @@ -69,24 +69,36 @@ |
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 | - |
30 | -tracks = [] |
31 | - |
32 | - |
33 | -def get_music_from_musique(): |
34 | +m5 = {'id': 'track_length', |
35 | + 'type': 'i', |
36 | + 'field': Unity.SchemaFieldType.OPTIONAL} |
37 | +m6 = {'id': 'track_number', |
38 | + 'type': 'i', |
39 | + 'field': Unity.SchemaFieldType.OPTIONAL} |
40 | +EXTRA_METADATA = [m1, m2, m3, m4, m5, m6] |
41 | + |
42 | +ROOT_SQL = '''SELECT value FROM attributes WHERE name = "root"''' |
43 | + |
44 | +SEARCH_SQL = '''SELECT tracks.title, tracks.path, artists.name, albums.title, tracks.year, tracks.track, tracks.duration |
45 | + FROM tracks, artists, albums |
46 | + WHERE tracks.album = albums.id AND albums.artist = artists.id |
47 | + AND (tracks.title LIKE '%%%s%%' OR albums.title LIKE '%%%s%%' OR artists.name LIKE '%%%s%%') |
48 | + ORDER BY track''' |
49 | + |
50 | +ALBUM_SQL = '''SELECT tracks.title, tracks.path, artists.name, albums.title, tracks.year, tracks.track, tracks.duration |
51 | + FROM tracks, artists, albums |
52 | + WHERE tracks.album = albums.id AND albums.artist = artists.id |
53 | + AND (albums.title LIKE '%%%s%%' AND artists.name LIKE '%%%s%%') |
54 | + ORDER BY track''' |
55 | + |
56 | + |
57 | +def get_music_from_musique(query): |
58 | ''' |
59 | Parses Musique's database into a form we can use |
60 | ''' |
61 | # Copy musique's database to a backup so we can run searches on that rather than the main database |
62 | tracks = [] |
63 | if not os.path.exists(MUSIQUE_DBFILE): |
64 | - self.tracks = tracks |
65 | return tracks |
66 | |
67 | shutil.copy2(MUSIQUE_DBFILE, MUSIQUE_BACKUP_DBFILE) |
68 | @@ -95,16 +107,9 @@ |
69 | try: |
70 | conn = sqlite3.connect(MUSIQUE_BACKUP_DBFILE) |
71 | cursor = conn.cursor() |
72 | - # Go through the safe and grab track names, their uris, the artist name, the album title and the track's mimetypes |
73 | - # 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 |
74 | - cursor.execute('''SELECT value |
75 | - FROM attributes |
76 | - WHERE name = "root"''') |
77 | + cursor.execute(ROOT_SQL) |
78 | root = cursor.fetchall() |
79 | - cursor.execute('''SELECT tracks.title, tracks.path, artists.name, albums.title, "taglib/mp3", tracks.year, tracks.track, tracks.duration |
80 | - FROM tracks, artists, albums |
81 | - WHERE tracks.album = albums.id AND albums.artist = artists.id |
82 | - ORDER BY track''') |
83 | + cursor.execute(query) |
84 | tracks = cursor.fetchall() |
85 | cursor.close() |
86 | except: |
87 | @@ -152,172 +157,77 @@ |
88 | ''' |
89 | Search for help documents matching the search string |
90 | ''' |
91 | - root = [] |
92 | results = [] |
93 | - root, tracks = get_music_from_musique() |
94 | - trackresults = [] |
95 | + root, tracks = get_music_from_musique(SEARCH_SQL % (search, search, search)) |
96 | albumresults = [] |
97 | for track in tracks: |
98 | - title = u"" if track[0] is None else str(track[0]) |
99 | - uri = u"" if track[1] is None else root[0][0] + "/" + str(track[1]) |
100 | - artist = u"" if track[2] is None else str(track[2]) |
101 | - album = u"" if track[3] is None else str(track[3]) |
102 | - mimetype = u"" if track[4] is None else str(track[4]) |
103 | - albumartist = u"" if track[2] is None else str(track[2]) |
104 | - year = 0 if track[5] is None else int(track[5]) |
105 | - genre = "" |
106 | - trackname = title + u" - " + album + u" - " + artist |
107 | - if search.lower() in trackname.lower(): |
108 | - albumart = get_album_art(track) |
109 | - albumuri = "album://" + albumartist + "/" + album |
110 | - if track not in trackresults: |
111 | - results.append({'uri': uri, |
112 | - 'icon': albumart, |
113 | - 'category': 0, |
114 | - 'mimetype': mimetype, |
115 | - 'title': title, |
116 | - 'comment': artist, |
117 | - 'album':GLib.Variant('s', album), |
118 | - 'artist':GLib.Variant('s', artist), |
119 | - 'genre':GLib.Variant('s', genre), |
120 | - 'year':GLib.Variant('i', year)}) |
121 | - trackresults.append(track) |
122 | + title = "" if track[0] is None else track[0] |
123 | + uri = "" if track[1] is None else root[0][0] + "/" + track[1] |
124 | + artist = "" if track[2] is None else track[2] |
125 | + album = "" if track[3] is None else track[3] |
126 | + albumartist = "" if track[2] is None else track[2] |
127 | + year = 0 if track[4] is None else track[4] |
128 | + track_length = 0 if track[6] is None else track[6] |
129 | + track_number = 0 if track[5] is None else track[5] |
130 | + albumart = get_album_art(track) |
131 | + albumuri = "album://" + albumartist + "/" + album |
132 | + results.append({'uri': uri, |
133 | + 'icon': albumart, |
134 | + 'category': 0, |
135 | + 'title': title, |
136 | + 'album':GLib.Variant('s', album), |
137 | + 'artist':GLib.Variant('s', artist), |
138 | + 'year':GLib.Variant('i', year), |
139 | + 'track_length': GLib.Variant('i', track_length), |
140 | + 'track_number': GLib.Variant('i', track_number)}) |
141 | |
142 | - if album not in albumresults: |
143 | - results.append({'uri': albumuri, |
144 | - 'icon': albumart, |
145 | - 'category': 1, |
146 | - 'mimetype': mimetype, |
147 | - 'title': album, |
148 | - 'comment': artist, |
149 | - 'album':GLib.Variant('s', album), |
150 | - 'artist':GLib.Variant('s', artist), |
151 | - 'genre':GLib.Variant('s', genre), |
152 | - 'year':GLib.Variant('i', year)}) |
153 | - albumresults.append(album) |
154 | + if album not in albumresults: |
155 | + results.append({'uri': albumuri, |
156 | + 'icon': albumart, |
157 | + 'category': 1, |
158 | + 'title': album, |
159 | + 'album':GLib.Variant('s', album), |
160 | + 'artist':GLib.Variant('s', artist), |
161 | + 'year':GLib.Variant('i', year), |
162 | + 'track_length': GLib.Variant('i', track_length), |
163 | + 'track_number': GLib.Variant('i', track_number)}) |
164 | + albumresults.append(album) |
165 | return results |
166 | |
167 | |
168 | -def activate(scope, uri): |
169 | - import subprocess |
170 | - albumtracks = [] |
171 | - albumtracks.append("audacious") |
172 | - albumtracks.append("-E") |
173 | - # If uri starts with album:// then we need to play all the songs on it |
174 | - if uri.startswith("album://"): |
175 | - for track in tracks: |
176 | - album = "album://" + track[2] + "/" + track[3] |
177 | - if not album.find(uri) == -1: |
178 | - albumtrack = urllib.parse.unquote(str(track[1])) |
179 | - albumtracks.append(albumtrack) |
180 | - subprocess.Popen(albumtracks) |
181 | - else: |
182 | - albumtracks.append(uri) |
183 | - subprocess.Popen(albumtracks) |
184 | - return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='') |
185 | - |
186 | - |
187 | -def show_in_folder(scope, uri): |
188 | - """ Shows the folder containing the selected track as requested from the Preview |
189 | - """ |
190 | - if uri.startswith("album://"): |
191 | - for track in tracks: |
192 | - album = "album://" + track[2] + "/" + track[3] |
193 | - if not album.find(uri) == -1: |
194 | - filename = track[1] |
195 | - continue |
196 | - else: |
197 | - filename = uri |
198 | - dirname = os.path.dirname(filename) |
199 | - dirname = dirname.replace("%20", "\ ") |
200 | - os.system("xdg-open '%s'" % str(dirname)) |
201 | - return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='') |
202 | - |
203 | - |
204 | -def preview_uri(scope, uri): |
205 | - """Preview request handler""" |
206 | - albumtracks = [] |
207 | - isalbum = False |
208 | - if uri.startswith("album://"): |
209 | - isalbum = True |
210 | - for track in tracks: |
211 | - album = "album://" + track[2] + "/" + track[3] |
212 | - if not album.find(uri) == -1: |
213 | - albumtracks.append(track) |
214 | - albumtracks.sort(key=lambda track: int(track[7])) |
215 | - else: |
216 | - for track in tracks: |
217 | - album = "file://" + track[1] |
218 | - if not album.find(uri) == -1: |
219 | - albumtracks.append(track) |
220 | - iteration = model.get_first_iter() |
221 | - end_iter = model.get_last_iter() |
222 | - while iteration != end_iter: |
223 | - if model.get_value(iteration, 0) == uri: |
224 | - title = model.get_value(iteration, 5) |
225 | - description = model.get_value(iteration, 6) |
226 | - if model.get_value(iteration, 1) == "musique": |
227 | - image = "file:///usr/share/icons/hicolor/scalable/apps/audacious.svg" |
228 | - else: |
229 | - image = "file://%s" % model.get_value(iteration, 1) |
230 | - |
231 | - preview = Unity.MusicPreview.new(title, description, None) |
232 | - preview.props.image_source_uri = image |
233 | - for albumtrack in albumtracks: |
234 | - if isalbum: |
235 | - track = Unity.TrackMetadata.full("file://" + urllib.parse.unquote(str(albumtrack[1])), # uri |
236 | - int(albumtrack[7]), # track number |
237 | - albumtrack[0], # track title |
238 | - albumtrack[2], # artist |
239 | - albumtrack[3], # album |
240 | - int(albumtrack[8]) / 1000) # track length |
241 | - else: |
242 | - preview = Unity.MusicPreview.new(albumtrack[0], "", None) |
243 | - preview.props.image_source_uri = image |
244 | - track = Unity.TrackMetadata.full("file://" + urllib.parse.unquote(str(albumtrack[1])), |
245 | - int(albumtrack[7]), |
246 | - albumtrack[0], |
247 | - albumtrack[2], |
248 | - albumtrack[3], |
249 | - int(albumtrack[8]) / 1000) |
250 | +class Preview(Unity.ResultPreviewer): |
251 | + |
252 | + def do_run(self): |
253 | + album = self.result.metadata['album'].get_string() |
254 | + artist = self.result.metadata['artist'].get_string() |
255 | + preview = Unity.MusicPreview.new(self.result.title, '', None) |
256 | + preview.props.image_source_uri = 'file://%s' % self.result.icon_hint |
257 | + preview.props.subtitle = self.result.metadata['artist'].get_string() |
258 | + if self.result.uri.startswith("album://"): |
259 | + root, tracks = get_music_from_musique(ALBUM_SQL % (album, artist)) |
260 | + for track in tracks: |
261 | + track = Unity.TrackMetadata.full('file://%s/%s' % (root[0][0], track[1]), |
262 | + track[5], |
263 | + track[0], |
264 | + track[2], |
265 | + track[3], |
266 | + track[6]) |
267 | preview.add_track(track) |
268 | - |
269 | - # Add the "Play" action |
270 | - play_action = Unity.PreviewAction.new("activate_uri", "Play", None) |
271 | - play_action.connect("activated", activate) |
272 | - preview.add_action(play_action) |
273 | - |
274 | - # Add the "Show in folder" action |
275 | - show_action = Unity.PreviewAction.new("show_in_folder", "Show In Folder", None) |
276 | - show_action.connect("activated", show_in_folder) |
277 | - preview.add_action(show_action) |
278 | - |
279 | - preview.connect("play", play) |
280 | - preview.connect("pause", pause) |
281 | - preview.connect("closed", closed) |
282 | - break |
283 | - iteration = model.next(iteration) |
284 | - if preview is None: |
285 | - print("Couldn't find model row for requested preview uri: '%s'", uri) |
286 | - return preview |
287 | - |
288 | - |
289 | -def play(preview, uri): |
290 | - """Plays the selected track as selected in the Preview""" |
291 | - player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) |
292 | - dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Play(uri) |
293 | - |
294 | - |
295 | -def pause(preview, uri): |
296 | - """Pauses the selected track as selected in the Preview""" |
297 | - player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) |
298 | - dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Pause() |
299 | - |
300 | - |
301 | -def closed(preview): |
302 | - """Stops playing when the previre is closed""" |
303 | - player = self.bus.get_object(PREVIEW_PLAYER_DBUS_NAME, PREVIEW_PLAYER_DBUS_PATH) |
304 | - dbus.Interface(player, PREVIEW_PLAYER_DBUS_IFACE).Close() |
305 | + else: |
306 | + track = Unity.TrackMetadata.full('file://%s' % self.result.uri, |
307 | + self.result.metadata['track_number'].get_int32(), |
308 | + self.result.title, |
309 | + self.result.metadata['artist'].get_string(), |
310 | + self.result.metadata['album'].get_string(), |
311 | + self.result.metadata['track_length'].get_int32()) |
312 | + preview.add_track(track) |
313 | + |
314 | + view_action = Unity.PreviewAction.new("play", _("Play"), None) |
315 | + preview.add_action(view_action) |
316 | + show_action = Unity.PreviewAction.new("show", _("Show in Folder"), None) |
317 | + preview.add_action(show_action) |
318 | + return preview |
319 | + |
320 | |
321 | # Classes below this point establish communication |
322 | # with Unity, you probably shouldn't modify them. |
323 | @@ -352,19 +262,8 @@ |
324 | i['comment'] = '' |
325 | if not 'dnd_uri' in i or not i['dnd_uri'] or i['dnd_uri'] == '': |
326 | i['dnd_uri'] = i['uri'] |
327 | - i['metadata'] = {} |
328 | - if EXTRA_METADATA: |
329 | - for e in i: |
330 | - for m in EXTRA_METADATA: |
331 | - if m['id'] == e: |
332 | - i['metadata'][e] = i[e] |
333 | - i['metadata']['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS) |
334 | - result = Unity.ScopeResult.create(str(i['uri']), str(i['icon']), |
335 | - i['category'], i['result_type'], |
336 | - str(i['mimetype']), str(i['title']), |
337 | - str(i['comment']), str(i['dnd_uri']), |
338 | - i['metadata']) |
339 | - result_set.add_result(result) |
340 | + i['provider_credits'] = GLib.Variant('s', PROVIDER_CREDITS) |
341 | + result_set.add_result(**i) |
342 | except Exception as error: |
343 | print(error) |
344 | |
345 | @@ -420,6 +319,29 @@ |
346 | se = MySearch(search_context) |
347 | return se |
348 | |
349 | + def do_activate(self, result, metadata, id): |
350 | + album = result.metadata['album'].get_string() |
351 | + artist = result.metadata['artist'].get_string() |
352 | + |
353 | + if id == 'show': |
354 | + if result.uri.startswith("album://"): |
355 | + tracks = get_music_from_musique(ALBUM_SQL % (album, artist)) |
356 | + filename = tracks[0][1].decode('utf-8') |
357 | + else: |
358 | + filename = result.uri |
359 | + dirname = os.path.dirname(filename) |
360 | + os.system("xdg-open '%s'" % str(dirname)) |
361 | + else: |
362 | + # musique doesn't take any command line arguments so all we can do is open the player |
363 | + os.system('musique') |
364 | + |
365 | + return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri=None) |
366 | + |
367 | + def do_create_previewer(self, result, metadata): |
368 | + rp = Preview() |
369 | + rp.set_scope_result(result) |
370 | + rp.set_search_metadata(metadata) |
371 | + return rp |
372 | |
373 | def load_scope(): |
374 | return Scope() |
PASSED: Continuous integration, rev:20 jenkins. qa.ubuntu. com/job/ unity-scope- musique- ci/1/ jenkins. qa.ubuntu. com/job/ unity-scope- musique- raring- amd64-ci/ 1
http://
Executed test runs:
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ unity-scope- musique- ci/1/rebuild
http://