~ricmm/+junk/unity-lens_music-sc

« back to all changes in this revision

Viewing changes to src/musicstore-collection.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 MusicStoreCollection : Object
 
25
  {
 
26
 
 
27
    private const string MUSICSTORE_BASE_URI = "http://musicsearch.ubuntu.com/v1/";
 
28
    private const uint PURCHASE_CATEGORY = 2;
 
29
    private HashTable<string, string> preview_uri_map; //maps u1ms store uri to details uri used by preview request
 
30
 
 
31
    public MusicStoreCollection ()
 
32
    {
 
33
      preview_uri_map = new HashTable<string, string>(str_hash, str_equal);
 
34
    }
 
35
 
 
36
    public async void search (LensSearch search, SearchType search_type,
 
37
                        owned List<FilterParser> filters, int max_results = -1, Cancellable cancellable) throws IOError
 
38
    {
 
39
      string? uri = build_search_uri (search.search_string, filters);
 
40
 
 
41
      if (uri == null)
 
42
        return;   
 
43
 
 
44
      preview_uri_map.remove_all ();
 
45
      var results_model = search.results_model;
 
46
      File file = File.new_for_uri (uri);
 
47
 
 
48
      yield read_musicstore_search_result_async (file, results_model, cancellable);
 
49
    }
 
50
 
 
51
    public void get_album_details (string uri, out Album album, out SList<Track> tracks)
 
52
    {
 
53
      string http_uri = uri.substring (7); // strip off "u1ms://" from the uri
 
54
      if (preview_uri_map.contains (http_uri))
 
55
      {
 
56
        string details_uri = preview_uri_map.get (http_uri);
 
57
        debug ("details uri: %s", details_uri);
 
58
 
 
59
        var file = File.new_for_uri (details_uri);
 
60
        var parser = new Json.Parser ();
 
61
 
 
62
        try {
 
63
          if (parser.load_from_stream (file.read (null))) //FIXME: make async
 
64
          {
 
65
            var root_obj = parser.get_root ().get_object ();
 
66
 
 
67
            album = new Album ();
 
68
            tracks = new SList<Track> ();
 
69
            album.title = root_obj.get_string_member ("title");
 
70
            album.artwork_path = root_obj.get_string_member ("image");
 
71
            album.artist = root_obj.get_string_member ("artist");
 
72
            album.uri = http_uri;
 
73
 
 
74
            if (root_obj.has_member ("tracks"))
 
75
            {
 
76
              var tracks_node = root_obj.get_array_member ("tracks");
 
77
              debug ("Album details: '%s', '%s'", uri, details_uri);
 
78
              foreach (var track_node in tracks_node.get_elements ())
 
79
              {
 
80
                var track_obj = track_node.get_object ();
 
81
                var track = new Track ();
 
82
                track.uri = track_obj.get_string_member ("preview");
 
83
                track.title = track_obj.get_string_member ("title");
 
84
                track.duration = (int)track_obj.get_member ("duration").get_int ();
 
85
                tracks.append (track);
 
86
              }
 
87
            }
 
88
            else // details for single track
 
89
            {
 
90
              debug ("Single track details: '%s', '%s'", uri, details_uri);
 
91
              var track = new Track ();
 
92
              track.uri = root_obj.get_string_member ("preview");
 
93
              track.title = root_obj.get_string_member ("title");
 
94
              track.duration = (int)root_obj.get_member ("duration").get_int ();
 
95
              tracks.append (track);
 
96
            }
 
97
          }
 
98
          else
 
99
          {
 
100
            warning ("Can't parse json data for '%s'", details_uri);
 
101
          }
 
102
        }
 
103
        catch (Error e)
 
104
        {
 
105
          warning ("Error fetching details for '%s': %s", details_uri, e.message);
 
106
        }
 
107
      }
 
108
      else
 
109
      {
 
110
        warning ("No details uri for '%s'", http_uri);
 
111
      }
 
112
    }
 
113
 
 
114
    private async void read_musicstore_search_result_async (File file, Dee.Model model, Cancellable cancellable)
 
115
    {
 
116
      var timer = new Timer ();
 
117
      debug ("Searching %s", file.get_uri ());
 
118
 
 
119
      try {
 
120
        var stream = yield file.read_async (Priority.DEFAULT, cancellable);
 
121
        var dis = new DataInputStream (stream);
 
122
        var parser = new Json.Parser ();
 
123
        yield parser.load_from_stream_async (dis, cancellable);
 
124
        var root_object = parser.get_root ().get_object ();
 
125
        
 
126
        Json.Object? results = root_object.get_object_member ("results");
 
127
        if (results == null) {
 
128
          warning ("Invalid response from server. No 'results' member.");
 
129
          return;
 
130
        }
 
131
        
 
132
        var albums = results.get_array_member ("album").get_elements ();
 
133
        var tracks = results.get_array_member ("track").get_elements ();
 
134
        
 
135
        debug ("Got %u albums and %u tracks", albums.length (), tracks.length ());
 
136
        
 
137
        unowned string? uri, details_uri, artwork_path, mimetype, title, artist, dnd_uri;
 
138
        
 
139
        foreach (var album_node in albums) {
 
140
          var album_obj = album_node.get_object ();
 
141
          
 
142
          uri = album_obj.get_string_member ("purchase_url");
 
143
          details_uri = album_obj.get_string_member ("details");
 
144
          artwork_path = album_obj.get_string_member ("image");
 
145
          mimetype = "audio-x-generic";
 
146
          title = album_obj.get_string_member ("title");
 
147
          artist = album_obj.get_string_member ("artist");
 
148
          dnd_uri = uri;
 
149
          
 
150
          model.append (uri, artwork_path, PURCHASE_CATEGORY,
 
151
                        mimetype, title, artist, dnd_uri);
 
152
 
 
153
      preview_uri_map.insert (uri.substring (7), details_uri); // strip off "u1ms://" from the uri
 
154
    }
 
155
        
 
156
        foreach (var track_node in tracks) {
 
157
          var track_obj = track_node.get_object ();
 
158
          
 
159
          uri = track_obj.get_string_member ("purchase_url");
 
160
          details_uri = track_obj.get_string_member ("details");
 
161
          artwork_path = track_obj.get_string_member ("image");
 
162
          mimetype = "audio-x-generic";
 
163
          title = track_obj.get_string_member ("title");
 
164
          artist = track_obj.get_string_member ("artist");
 
165
          dnd_uri = uri;
 
166
          
 
167
          // FIXME drag n drop uri needs to be the u1ms:// link
 
168
          
 
169
          model.append (uri, artwork_path, PURCHASE_CATEGORY,
 
170
                        mimetype, title, artist, dnd_uri);
 
171
      preview_uri_map.insert (uri.substring (7), details_uri); // strip off "u1ms://" from the uri
 
172
    }
 
173
 
 
174
        debug ("Retrieved '%s' in %fms", file.get_uri (), timer.elapsed()*1000);
 
175
        debug ("Model has %u rows after search", model.get_n_rows ());
 
176
 
 
177
      } catch (Error e) {
 
178
        warning ("Error reading URL '%s': %s", file.get_uri (), e.message);
 
179
      }
 
180
    }
 
181
 
 
182
    private string? build_search_uri (string query, List<FilterParser> filters)
 
183
    {
 
184
      if (query.strip() == "")
 
185
        return null;
 
186
    
 
187
      MusicStoreFilterParser store_parser;
 
188
      string musicstore_base_uri = MUSICSTORE_BASE_URI;
 
189
      if (GLib.Environment.get_variable("MUSICSTORE_URI") != null)
 
190
        musicstore_base_uri = GLib.Environment.get_variable("MUSICSTORE_URI");
 
191
      debug ("Using base URI of '%s'", musicstore_base_uri);
 
192
      StringBuilder uri = new StringBuilder (musicstore_base_uri);
 
193
      uri.append ("search?q=");
 
194
      uri.append (Uri.escape_string (query, "", false));
 
195
 
 
196
      foreach (FilterParser parser in filters) {
 
197
          if (parser is GenreFilterParser)
 
198
            store_parser = new MusicStoreGenreFilterParser (parser as GenreFilterParser);
 
199
          else if (parser is DecadeFilterParser)
 
200
            store_parser = new MusicStoreDecadeFilterParser (parser as DecadeFilterParser);
 
201
          else
 
202
            continue;
 
203
 
 
204
          uri.append (store_parser.parse ());
 
205
      }
 
206
      
 
207
      uri.append ("&pagesize=10");
 
208
      
 
209
      // This makes the service return $pagesize results *per content type*.
 
210
      // Which we need - as it could otherwise return $pagesize results mixed
 
211
      // or artist,album, and track. Since we can't display artists, this can
 
212
      // lead to an empty result set in the dash if there is only artists in
 
213
      // the response from the webservice
 
214
      uri.append ("&grouping=1");
 
215
 
 
216
      return uri.str;
 
217
    }
 
218
  }
 
219
}