~ubuntu-branches/ubuntu/saucy/clementine/saucy

« back to all changes in this revision

Viewing changes to src/globalsearch/librarysearchprovider.cpp

  • Committer: Package Import Robot
  • Author(s): Thomas PIERSON
  • Date: 2012-01-01 20:43:39 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20120101204339-lsb6nndwhfy05sde
Tags: 1.0.1+dfsg-1
New upstream release. (Closes: #653926, #651611, #657391)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of Clementine.
 
2
   Copyright 2010, David Sansome <me@davidsansome.com>
 
3
 
 
4
   Clementine is free software: you can redistribute it and/or modify
 
5
   it under the terms of the GNU General Public License as published by
 
6
   the Free Software Foundation, either version 3 of the License, or
 
7
   (at your option) any later version.
 
8
 
 
9
   Clementine is distributed in the hope that it will be useful,
 
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
   GNU General Public License for more details.
 
13
 
 
14
   You should have received a copy of the GNU General Public License
 
15
   along with Clementine.  If not, see <http://www.gnu.org/licenses/>.
 
16
*/
 
17
 
 
18
#include "librarysearchprovider.h"
 
19
#include "core/logging.h"
 
20
#include "covers/albumcoverloader.h"
 
21
#include "library/librarybackend.h"
 
22
#include "library/libraryquery.h"
 
23
#include "library/sqlrow.h"
 
24
#include "playlist/songmimedata.h"
 
25
 
 
26
 
 
27
LibrarySearchProvider::LibrarySearchProvider(LibraryBackendInterface* backend,
 
28
                                             const QString& name,
 
29
                                             const QString& id,
 
30
                                             const QIcon& icon,
 
31
                                             bool enabled_by_default,
 
32
                                             QObject* parent)
 
33
  : BlockingSearchProvider(parent),
 
34
    backend_(backend)
 
35
{
 
36
  Hints hints = WantsSerialisedArtQueries | ArtIsInSongMetadata |
 
37
                CanGiveSuggestions;
 
38
 
 
39
  if (!enabled_by_default) {
 
40
    hints |= DisabledByDefault;
 
41
  }
 
42
 
 
43
  Init(name, id, icon, hints);
 
44
}
 
45
 
 
46
SearchProvider::ResultList LibrarySearchProvider::Search(int id, const QString& query) {
 
47
  QueryOptions options;
 
48
  options.set_filter(query);
 
49
 
 
50
  LibraryQuery q(options);
 
51
  q.SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec);
 
52
 
 
53
  if (!backend_->ExecQuery(&q)) {
 
54
    return ResultList();
 
55
  }
 
56
 
 
57
  const QStringList tokens = TokenizeQuery(query);
 
58
 
 
59
  QMultiMap<QString, Song> albums;
 
60
  QSet<QString> albums_with_non_track_matches;
 
61
 
 
62
  ResultList ret;
 
63
 
 
64
  while (q.Next()) {
 
65
    Song song;
 
66
    song.InitFromQuery(q, true);
 
67
 
 
68
    QString album_key = song.album();
 
69
    if (song.is_compilation() && !song.albumartist().isEmpty()) {
 
70
      album_key.prepend(song.albumartist() + " - ");
 
71
    } else if (!song.is_compilation()) {
 
72
      album_key.prepend(song.artist());
 
73
    }
 
74
 
 
75
    globalsearch::MatchQuality quality = MatchQuality(tokens, song.title());
 
76
 
 
77
    if (quality != globalsearch::Quality_None) {
 
78
      // If the query matched in the song title then we're interested in this
 
79
      // as an individual song.
 
80
      Result result(this);
 
81
      result.type_ = globalsearch::Type_Track;
 
82
      result.metadata_ = song;
 
83
      result.match_quality_ = quality;
 
84
      ret << result;
 
85
    } else {
 
86
      // Otherwise we record this as being an interesting album.
 
87
      albums_with_non_track_matches.insert(album_key);
 
88
    }
 
89
 
 
90
    albums.insertMulti(album_key, song);
 
91
  }
 
92
 
 
93
  // Add any albums that contained least one song that wasn't matched on the
 
94
  // song title.
 
95
  foreach (const QString& key, albums_with_non_track_matches) {
 
96
    Result result(this);
 
97
    result.type_ = globalsearch::Type_Album;
 
98
    result.metadata_ = albums.value(key);
 
99
    result.album_size_ = albums.count(key);
 
100
    result.match_quality_ =
 
101
        qMin(
 
102
          MatchQuality(tokens, result.metadata_.albumartist()),
 
103
          qMin(MatchQuality(tokens, result.metadata_.artist()),
 
104
               MatchQuality(tokens, result.metadata_.album())));
 
105
 
 
106
    result.album_songs_ = albums.values(key);
 
107
    SortSongs(&result.album_songs_);
 
108
 
 
109
    ret << result;
 
110
  }
 
111
 
 
112
  return ret;
 
113
}
 
114
 
 
115
void LibrarySearchProvider::LoadTracksAsync(int id, const Result& result) {
 
116
  SongList ret;
 
117
 
 
118
  switch (result.type_) {
 
119
  case globalsearch::Type_Track:
 
120
    // This is really easy - we just emit the track again.
 
121
    ret << result.metadata_;
 
122
    break;
 
123
 
 
124
  case globalsearch::Type_Album: {
 
125
    // Find all the songs in this album.
 
126
    LibraryQuery query;
 
127
    query.SetColumnSpec("ROWID, " + Song::kColumnSpec);
 
128
    query.AddCompilationRequirement(result.metadata_.is_compilation());
 
129
    query.AddWhere("album", result.metadata_.album());
 
130
 
 
131
    if (!result.metadata_.is_compilation())
 
132
      query.AddWhere("artist", result.metadata_.artist());
 
133
 
 
134
    if (!backend_->ExecQuery(&query)) {
 
135
      break;
 
136
    }
 
137
 
 
138
    while (query.Next()) {
 
139
      Song song;
 
140
      song.InitFromQuery(query, true);
 
141
      ret << song;
 
142
    }
 
143
  }
 
144
 
 
145
  default:
 
146
    break;
 
147
  }
 
148
 
 
149
  SortSongs(&ret);
 
150
 
 
151
  SongMimeData* mime_data = new SongMimeData;
 
152
  mime_data->backend = backend_;
 
153
  mime_data->songs = ret;
 
154
 
 
155
  emit TracksLoaded(id, mime_data);
 
156
}
 
157
 
 
158
QString LibrarySearchProvider::GetSuggestion() {
 
159
  // We'd like to use order by random(), but that's O(n) in sqlite, so instead
 
160
  // get the largest ROWID and pick a couple of random numbers within that
 
161
  // range.
 
162
 
 
163
  LibraryQuery q;
 
164
  q.SetColumnSpec("ROWID");
 
165
  q.SetOrderBy("ROWID DESC");
 
166
  q.SetIncludeUnavailable(true);
 
167
  q.SetLimit(1);
 
168
 
 
169
  if (!backend_->ExecQuery(&q) || !q.Next()) {
 
170
    return QString();
 
171
  }
 
172
 
 
173
  const int largest_rowid = q.Value(0).toInt();
 
174
 
 
175
  for (int attempt=0 ; attempt<10 ; ++attempt) {
 
176
    LibraryQuery q;
 
177
    q.SetColumnSpec("artist, album");
 
178
    q.SetIncludeUnavailable(true);
 
179
    q.AddWhere("ROWID", qrand() % largest_rowid);
 
180
    q.SetLimit(1);
 
181
 
 
182
    if (!backend_->ExecQuery(&q) || !q.Next()) {
 
183
      continue;
 
184
    }
 
185
 
 
186
    const QString artist = q.Value(0).toString();
 
187
    const QString album  = q.Value(1).toString();
 
188
 
 
189
    if (!artist.isEmpty() && !album.isEmpty())
 
190
      return (qrand() % 2 == 0) ? artist : album;
 
191
    else if (!artist.isEmpty())
 
192
      return artist;
 
193
    else if (!album.isEmpty())
 
194
      return album;
 
195
  }
 
196
 
 
197
  return QString();
 
198
}