Merge lp:~submarine/unity-scope-musique/musique-previews into lp:unity-scope-musique

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
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()

Subscribers

People subscribed via source and target branches

to all changes: