~ricmm/+junk/unity-lens_music-sc

« back to all changes in this revision

Viewing changes to src/rhythmbox-scope.vala

  • Committer: Ricardo Mendoza
  • Date: 2012-09-05 14:20:15 UTC
  • Revision ID: ricardo.mendoza@canonical.com-20120905142015-prem6hiyfshwgm8q
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2011 Canonical Ltd
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License version 3 as
 
6
 * published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored by Alex Launi <alex.launi@canonical.com>
 
17
 *
 
18
 */
 
19
 
 
20
using GLib;
 
21
 
 
22
namespace Unity.MusicLens {
 
23
     
 
24
  public class RhythmboxScope : SimpleScope
 
25
  { 
 
26
    private RhythmboxCollection collection;
 
27
    private bool db_ready;
 
28
    private FileMonitor rb_xml_monitor;
 
29
    private PreviewPlayer preview_player;
 
30
    private Unity.MusicPreview? music_preview;
 
31
 
 
32
    public RhythmboxScope ()
 
33
    {
 
34
      base ();
 
35
 
 
36
      scope = new Unity.Scope ("/com/canonical/unity/scope/rhythmbox");
 
37
      scope.search_in_global = true;
 
38
      scope.activate_uri.connect (activate);
 
39
      scope.preview_uri.connect (preview);
 
40
      scope.sources.add_option ("rhythmbox", _("Rhythmbox"), null);
 
41
 
 
42
      base.initialize ();
 
43
      collection = new RhythmboxCollection ();
 
44
      db_ready = false;
 
45
    }
 
46
 
 
47
    protected override int num_results_without_search { get {return 100; } }
 
48
    protected override int num_results_global_search { get { return 20; } }
 
49
    protected override int num_results_lens_search { get { return 50; } }
 
50
 
 
51
    private async void prepare_rhythmbox_play_queue (string[] exec_args)
 
52
    {
 
53
      var main_exec = exec_args; //needed from vala >= 0.15.1: captured parameters of array type are no more copied implicitly
 
54
      Thread.create<void> (() =>
 
55
      {
 
56
        try
 
57
        {
 
58
          Process.spawn_command_line_sync ("rhythmbox-client --pause");
 
59
          Process.spawn_command_line_sync ("rhythmbox-client --clear-queue");
 
60
          Process.spawn_sync (null, main_exec, null, SpawnFlags.SEARCH_PATH,
 
61
                              null, null);
 
62
          Process.spawn_command_line_sync ("rhythmbox-client --next");
 
63
          // make sure we're playing
 
64
          Process.spawn_command_line_sync ("rhythmbox-client --play");
 
65
        }
 
66
        catch (Error err)
 
67
        {
 
68
          warning ("%s", err.message);
 
69
        }
 
70
        Idle.add (prepare_rhythmbox_play_queue.callback);
 
71
      }, false);
 
72
      yield;
 
73
    }
 
74
 
 
75
    public Unity.Preview preview (string uri)
 
76
    {
 
77
      music_preview = null;
 
78
      GLib.ThemedIcon? icon_file = null;
 
79
      
 
80
      if (Uri.parse_scheme (uri) == "album")
 
81
      {
 
82
        string album_key = uri.substring (8);
 
83
        
 
84
        foreach (unowned Track track in collection.get_album_tracks_detailed (album_key))
 
85
        {
 
86
          if (music_preview == null)
 
87
          {
 
88
            if (track.artwork_path != null)
 
89
            {
 
90
              icon_file = new GLib.ThemedIcon(track.artwork_path);
 
91
            }
 
92
            music_preview = new Unity.MusicPreview (track.album, track.album_artist, icon_file);
 
93
 
 
94
            var play_action = new Unity.PreviewAction ("play_album", _("Play"), null);
 
95
            play_action.activated.connect (play_album_preview);
 
96
            music_preview.add_action (play_action);
 
97
 
 
98
            var show_folder_action = new Unity.PreviewAction ("show_in_folder", _("Show in Folder"), null);
 
99
            show_folder_action.activated.connect (show_in_folder);
 
100
            music_preview.add_action (show_folder_action);
 
101
          }
 
102
          var tm = new Unity.TrackMetadata();
 
103
          tm.uri = track.uri;
 
104
          tm.track_no = track.track_number;
 
105
          tm.title = track.title;
 
106
          tm.length = track.duration;
 
107
          music_preview.add_track(tm);
 
108
        }
 
109
      }
 
110
      else if (Uri.parse_scheme (uri) == "file") // preview single track, ignore radio stations
 
111
      {
 
112
        Track? track = collection.get_album_track (uri);
 
113
        if (track != null)
 
114
        {
 
115
          if (track.artwork_path != null)
 
116
          {
 
117
            icon_file = new GLib.ThemedIcon(track.artwork_path);
 
118
          }
 
119
          music_preview = new Unity.MusicPreview (track.title, track.album_artist, icon_file);
 
120
 
 
121
          var play_action = new Unity.PreviewAction ("play_track", _("Play"), null);
 
122
          play_action.activated.connect (play_track_in_rhythmbox);
 
123
          music_preview.add_action (play_action);
 
124
 
 
125
          var show_folder_action = new Unity.PreviewAction ("show_in_folder", _("Show in Folder"), null);
 
126
          show_folder_action.activated.connect (show_in_folder);
 
127
          music_preview.add_action (show_folder_action);
 
128
          
 
129
          var tm = new Unity.TrackMetadata();
 
130
          tm.uri = track.uri;
 
131
          tm.track_no = track.track_number;
 
132
          tm.title = track.title;
 
133
          tm.length = track.duration;
 
134
          music_preview.add_track(tm);
 
135
        }
 
136
      }
 
137
 
 
138
      if (preview != null)
 
139
      {
 
140
        music_preview.play.connect (play);
 
141
        music_preview.pause.connect (pause);
 
142
        music_preview.closed.connect (closed);
 
143
      }
 
144
      
 
145
      return music_preview;
 
146
    }
 
147
 
 
148
    private Unity.ActivationResponse show_in_folder (string uri)
 
149
    {
 
150
      File? track_file = null;
 
151
 
 
152
      if (Uri.parse_scheme (uri) == "album")
 
153
      {
 
154
        string album_key = uri.substring (8);
 
155
        var tracks = collection.get_album_tracks (album_key);
 
156
        if (tracks.length () > 0)
 
157
        {
 
158
          track_file = File.new_for_uri (tracks.nth_data (0));
 
159
        }
 
160
      }
 
161
      else //file
 
162
      {
 
163
        track_file = File.new_for_uri (uri);
 
164
      }
 
165
 
 
166
      File? parent_dir = track_file.get_parent ();
 
167
      if (parent_dir != null)
 
168
      {
 
169
        try
 
170
        {
 
171
          GLib.AppInfo.launch_default_for_uri (parent_dir.get_uri (), null);
 
172
          return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH);
 
173
        }
 
174
        catch (GLib.Error e)
 
175
        {
 
176
          warning ("Failed to launch application for uri '%s': %s", parent_dir.get_uri (), e.message);
 
177
        }
 
178
      }
 
179
      else
 
180
      {
 
181
        warning ("Failed to get parent dir for uri: '%s'", uri);
 
182
      }
 
183
      return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED);
 
184
    }
 
185
 
 
186
    private Unity.ActivationResponse play_track_in_rhythmbox (string uri)
 
187
    {
 
188
      return activate (uri);
 
189
    }
 
190
 
 
191
    private Unity.ActivationResponse play_album_preview (string uri)
 
192
    {
 
193
      return activate (uri);
 
194
    }
 
195
 
 
196
    public void closed (Unity.Preview preview)
 
197
    {
 
198
      if (preview_player != null)
 
199
      {
 
200
        try
 
201
        {
 
202
          preview_player.close ();
 
203
        }
 
204
        catch (IOError e)
 
205
        {
 
206
          warning ("Failed to close preview player: %s", e.message);
 
207
        }
 
208
      }
 
209
    }
 
210
 
 
211
    public void play (Unity.Preview preview, string uri)
 
212
    {
 
213
      debug ("play request: '%s'", uri);
 
214
      
 
215
      if (preview_player == null)
 
216
      {
 
217
        preview_player = new PreviewPlayer ();
 
218
        preview_player.progress.connect (on_progress_changed);
 
219
        preview_player.connect_to ();
 
220
      }
 
221
 
 
222
      // we will receive state back in on_progress_changed, but set it now so that it's immediately reflected in the dash
 
223
      music_preview.current_track_uri = uri;
 
224
      music_preview.current_progress = 0.0f;
 
225
      music_preview.current_track_state = Unity.MusicPreview.TrackState.PLAYING;
 
226
 
 
227
      preview_player.play (uri);
 
228
    }
 
229
    
 
230
    public void pause (Unity.Preview preview, string uri)
 
231
    {
 
232
      debug ("pause request: '%s'", uri);
 
233
      if (preview_player != null)
 
234
      {
 
235
        // we will receive state back in on_progress_changed, but set it now so that it's immediately reflected in the dash
 
236
        music_preview.current_track_uri = uri;
 
237
        music_preview.current_track_state = Unity.MusicPreview.TrackState.PAUSED;
 
238
 
 
239
        preview_player.pause ();
 
240
      }
 
241
    }
 
242
 
 
243
    private void on_progress_changed (string uri, Unity.MusicPreview.TrackState state, double progress)
 
244
    {
 
245
      if (preview != null)
 
246
      {
 
247
        music_preview.current_track_uri = uri;
 
248
        music_preview.current_track_state = state;
 
249
        music_preview.current_progress = (float)progress;
 
250
      }
 
251
    }
 
252
 
 
253
    /**
 
254
     * Tells rhythmbox to play the selected uri(s)
 
255
     */
 
256
    public Unity.ActivationResponse activate (string uri)
 
257
    {
 
258
      string[] exec = {"rhythmbox-client"};
 
259
 
 
260
      try 
 
261
      {
 
262
        if (Uri.parse_scheme (uri) == "album")
 
263
        {
 
264
            // this will be a bit crazy, let's do it in a separate thread
 
265
            exec += "--enqueue";
 
266
            string album_key = uri.substring (8);
 
267
            foreach (unowned string track_uri in collection.get_album_tracks (album_key))
 
268
            {
 
269
                exec += track_uri;
 
270
            }
 
271
            exec += null;
 
272
            prepare_rhythmbox_play_queue.begin (exec);
 
273
            return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH);
 
274
        }
 
275
        else
 
276
        {
 
277
            exec += "--play-uri";
 
278
            exec += uri;
 
279
        }
 
280
 
 
281
        exec += null;
 
282
 
 
283
        debug ("Spawning rb '%s'", string.joinv (" ", exec));
 
284
        Process.spawn_async (null, 
 
285
                 exec,
 
286
                 null, 
 
287
                 SpawnFlags.SEARCH_PATH,
 
288
                 null, 
 
289
                 null);
 
290
    
 
291
        return new Unity.ActivationResponse (Unity.HandledType.HIDE_DASH);
 
292
      } catch (SpawnError error) {
 
293
        warning ("Failed to launch URI %s", uri);
 
294
        return new Unity.ActivationResponse (Unity.HandledType.NOT_HANDLED);
 
295
      }
 
296
    }
 
297
 
 
298
    public override async void perform_search (LensSearch search,
 
299
                                               SearchType search_type, 
 
300
                                               owned List<FilterParser> filters,
 
301
                                               int max_results = -1,
 
302
                                               Cancellable? cancellable = null)
 
303
    {
 
304
      int category_override = -1;
 
305
      if (search_type == SearchType.GLOBAL)
 
306
      {
 
307
        category_override = Category.MUSIC;
 
308
        // the lens shouldn't display anything for empty search
 
309
        if (is_search_empty (search)) return;
 
310
      }
 
311
 
 
312
      if (!db_ready)
 
313
      {
 
314
        // parse the DB lazily
 
315
        var tdb_path = Path.build_filename (Environment.get_user_cache_dir (),
 
316
                                            "rhythmbox", "album-art",
 
317
                                            "store.tdb");
 
318
        collection.parse_metadata_file (tdb_path);
 
319
 
 
320
        var xml_path = Path.build_filename (Environment.get_user_data_dir (),
 
321
                                            "rhythmbox", "rhythmdb.xml");
 
322
        collection.parse_file (xml_path);
 
323
        if (rb_xml_monitor == null)
 
324
        {
 
325
          // re-parse the file if it changes
 
326
          File xml_file = File.new_for_path (xml_path);
 
327
          try
 
328
          {
 
329
            rb_xml_monitor = xml_file.monitor (FileMonitorFlags.NONE);
 
330
            rb_xml_monitor.changed.connect (() => { db_ready = false; });
 
331
          }
 
332
          catch (Error err)
 
333
          {
 
334
            warning ("%s", err.message);
 
335
          }
 
336
        }
 
337
 
 
338
        db_ready = true;
 
339
      }
 
340
 
 
341
      collection.search (search, search_type, filters,
 
342
                         max_results, category_override);
 
343
    }
 
344
  }
 
345
}