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

« back to all changes in this revision

Viewing changes to src/internet/magnatuneservice.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 "magnatunedownloaddialog.h"
 
19
#include "magnatuneplaylistitem.h"
 
20
#include "magnatuneservice.h"
 
21
#include "magnatuneurlhandler.h"
 
22
#include "internetmodel.h"
 
23
#include "core/logging.h"
 
24
#include "core/mergedproxymodel.h"
 
25
#include "core/network.h"
 
26
#include "core/player.h"
 
27
#include "core/song.h"
 
28
#include "core/taskmanager.h"
 
29
#include "core/timeconstants.h"
 
30
#include "globalsearch/globalsearch.h"
 
31
#include "globalsearch/librarysearchprovider.h"
 
32
#include "library/librarymodel.h"
 
33
#include "library/librarybackend.h"
 
34
#include "library/libraryfilterwidget.h"
 
35
#include "ui/iconloader.h"
 
36
#include "ui/settingsdialog.h"
 
37
 
 
38
#include "qtiocompressor.h"
 
39
 
 
40
#include <QNetworkAccessManager>
 
41
#include <QNetworkRequest>
 
42
#include <QNetworkReply>
 
43
#include <QXmlStreamReader>
 
44
#include <QSortFilterProxyModel>
 
45
#include <QMenu>
 
46
#include <QDesktopServices>
 
47
#include <QCoreApplication>
 
48
#include <QSettings>
 
49
 
 
50
#include <QtDebug>
 
51
 
 
52
using boost::shared_ptr;
 
53
 
 
54
const char* MagnatuneService::kServiceName = "Magnatune";
 
55
const char* MagnatuneService::kSettingsGroup = "Magnatune";
 
56
const char* MagnatuneService::kSongsTable = "magnatune_songs";
 
57
const char* MagnatuneService::kFtsTable = "magnatune_songs_fts";
 
58
 
 
59
const char* MagnatuneService::kHomepage = "http://magnatune.com";
 
60
const char* MagnatuneService::kDatabaseUrl = "http://magnatune.com/info/song_info_xml.gz";
 
61
const char* MagnatuneService::kStreamingHostname = "streaming.magnatune.com";
 
62
const char* MagnatuneService::kDownloadHostname = "download.magnatune.com";
 
63
 
 
64
const char* MagnatuneService::kPartnerId = "clementine";
 
65
const char* MagnatuneService::kDownloadUrl = "http://download.magnatune.com/buy/membership_free_dl_xml";
 
66
 
 
67
MagnatuneService::MagnatuneService(InternetModel* parent)
 
68
  : InternetService(kServiceName, parent, parent),
 
69
    url_handler_(new MagnatuneUrlHandler(this, this)),
 
70
    context_menu_(NULL),
 
71
    root_(NULL),
 
72
    library_backend_(NULL),
 
73
    library_model_(NULL),
 
74
    library_filter_(NULL),
 
75
    library_sort_model_(new QSortFilterProxyModel(this)),
 
76
    load_database_task_id_(0),
 
77
    membership_(Membership_None),
 
78
    format_(Format_Ogg),
 
79
    total_song_count_(0),
 
80
    network_(new NetworkAccessManager(this))
 
81
{
 
82
  // Create the library backend in the database thread
 
83
  library_backend_ = new LibraryBackend;
 
84
  library_backend_->moveToThread(parent->db_thread());
 
85
  library_backend_->Init(parent->db_thread()->Worker(), kSongsTable,
 
86
                         QString::null, QString::null, kFtsTable);
 
87
  library_model_ = new LibraryModel(library_backend_, parent->task_manager(), this);
 
88
 
 
89
  connect(library_backend_, SIGNAL(TotalSongCountUpdated(int)),
 
90
          SLOT(UpdateTotalSongCount(int)));
 
91
 
 
92
  library_sort_model_->setSourceModel(library_model_);
 
93
  library_sort_model_->setSortRole(LibraryModel::Role_SortText);
 
94
  library_sort_model_->setDynamicSortFilter(true);
 
95
  library_sort_model_->sort(0);
 
96
 
 
97
  model()->player()->RegisterUrlHandler(url_handler_);
 
98
  model()->global_search()->AddProvider(new LibrarySearchProvider(
 
99
      library_backend_,
 
100
      tr("Magnatune"),
 
101
      "magnatune",
 
102
      QIcon(":/providers/magnatune.png"),
 
103
      true,
 
104
      this));
 
105
}
 
106
 
 
107
MagnatuneService::~MagnatuneService() {
 
108
  delete context_menu_;
 
109
}
 
110
 
 
111
void MagnatuneService::ReloadSettings() {
 
112
  QSettings s;
 
113
  s.beginGroup(kSettingsGroup);
 
114
 
 
115
  membership_ = MembershipType(s.value("membership", Membership_None).toInt());
 
116
  username_ = s.value("username").toString();
 
117
  password_ = s.value("password").toString();
 
118
  format_ = PreferredFormat(s.value("format", Format_Ogg).toInt());
 
119
}
 
120
 
 
121
QStandardItem* MagnatuneService::CreateRootItem() {
 
122
  root_ = new QStandardItem(QIcon(":/providers/magnatune.png"), kServiceName);
 
123
  root_->setData(true, InternetModel::Role_CanLazyLoad);
 
124
  return root_;
 
125
}
 
126
 
 
127
void MagnatuneService::LazyPopulate(QStandardItem* item) {
 
128
  switch (item->data(InternetModel::Role_Type).toInt()) {
 
129
    case InternetModel::Type_Service:
 
130
      library_model_->Init();
 
131
      model()->merged_model()->AddSubModel(item->index(), library_sort_model_);
 
132
      break;
 
133
 
 
134
    default:
 
135
      break;
 
136
  }
 
137
}
 
138
 
 
139
void MagnatuneService::UpdateTotalSongCount(int count) {
 
140
  total_song_count_ = count;
 
141
  if (total_song_count_ == 0 && !load_database_task_id_) {
 
142
    ReloadDatabase();
 
143
  }
 
144
}
 
145
 
 
146
void MagnatuneService::ReloadDatabase() {
 
147
  QNetworkRequest request = QNetworkRequest(QUrl(kDatabaseUrl));
 
148
  request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
 
149
                       QNetworkRequest::AlwaysNetwork);
 
150
 
 
151
  QNetworkReply* reply = network_->get(request);
 
152
  connect(reply, SIGNAL(finished()), SLOT(ReloadDatabaseFinished()));
 
153
  
 
154
  if (!load_database_task_id_)
 
155
    load_database_task_id_ = model()->task_manager()->StartTask(
 
156
        tr("Downloading Magnatune catalogue"));
 
157
}
 
158
 
 
159
void MagnatuneService::ReloadDatabaseFinished() {
 
160
  QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
 
161
 
 
162
  model()->task_manager()->SetTaskFinished(load_database_task_id_);
 
163
  load_database_task_id_ = 0;
 
164
 
 
165
  if (reply->error() != QNetworkReply::NoError) {
 
166
    // TODO: Error handling
 
167
    qLog(Error) << reply->errorString();
 
168
    return;
 
169
  }
 
170
 
 
171
  if (root_->hasChildren())
 
172
    root_->removeRows(0, root_->rowCount());
 
173
 
 
174
  // The XML file is compressed
 
175
  QtIOCompressor gzip(reply);
 
176
  gzip.setStreamFormat(QtIOCompressor::GzipFormat);
 
177
  if (!gzip.open(QIODevice::ReadOnly)) {
 
178
    qLog(Warning) << "Error opening gzip stream";
 
179
    return;
 
180
  }
 
181
 
 
182
  // Remove all existing songs in the database
 
183
  library_backend_->DeleteAll();
 
184
 
 
185
  // Parse the XML we got from Magnatune
 
186
  QXmlStreamReader reader(&gzip);
 
187
  SongList songs;
 
188
  while (!reader.atEnd()) {
 
189
    reader.readNext();
 
190
 
 
191
    if (reader.tokenType() == QXmlStreamReader::StartElement &&
 
192
        reader.name() == "Track") {
 
193
      songs << ReadTrack(reader);
 
194
    }
 
195
  }
 
196
 
 
197
  // Add the songs to the database
 
198
  library_backend_->AddOrUpdateSongs(songs);
 
199
}
 
200
 
 
201
Song MagnatuneService::ReadTrack(QXmlStreamReader& reader) {
 
202
  Song song;
 
203
 
 
204
  while (!reader.atEnd()) {
 
205
    reader.readNext();
 
206
 
 
207
    if (reader.tokenType() == QXmlStreamReader::EndElement)
 
208
      break;
 
209
 
 
210
    if (reader.tokenType() == QXmlStreamReader::StartElement) {
 
211
      QStringRef name = reader.name();
 
212
      QString value = ReadElementText(reader);
 
213
 
 
214
      if (name == "artist")          song.set_artist(value);
 
215
      if (name == "albumname")       song.set_album(value);
 
216
      if (name == "trackname")       song.set_title(value);
 
217
      if (name == "tracknum")        song.set_track(value.toInt());
 
218
      if (name == "year")            song.set_year(value.toInt());
 
219
      if (name == "magnatunegenres") song.set_genre(value.section(',', 0, 0));
 
220
      if (name == "seconds")         song.set_length_nanosec(value.toInt() * kNsecPerSec);
 
221
      if (name == "cover_small")     song.set_art_automatic(value);
 
222
      if (name == "albumsku")        song.set_comment(value);
 
223
      if (name == "url") {
 
224
        QUrl url;
 
225
        // Magnatune's URLs are already encoded
 
226
        url.setEncodedUrl(value.toLocal8Bit());
 
227
        url.setScheme("magnatune");
 
228
        song.set_url(url);
 
229
      }
 
230
    }
 
231
  }
 
232
 
 
233
  song.set_valid(true);
 
234
  song.set_filetype(Song::Type_Stream);
 
235
 
 
236
  // We need to set these to satisfy the database constraints
 
237
  song.set_directory_id(0);
 
238
  song.set_mtime(0);
 
239
  song.set_ctime(0);
 
240
  song.set_filesize(0);
 
241
 
 
242
  return song;
 
243
}
 
244
 
 
245
// TODO: Replace with readElementText(SkipChildElements) in Qt 4.6
 
246
QString MagnatuneService::ReadElementText(QXmlStreamReader& reader) {
 
247
  int level = 1;
 
248
  QString ret;
 
249
  while (!reader.atEnd()) {
 
250
    switch (reader.readNext()) {
 
251
      case QXmlStreamReader::StartElement: level++; break;
 
252
      case QXmlStreamReader::EndElement:   level--; break;
 
253
      case QXmlStreamReader::Characters:
 
254
        ret += reader.text().toString().trimmed();
 
255
        break;
 
256
      default: break;
 
257
    }
 
258
 
 
259
    if (level == 0)
 
260
      break;
 
261
  }
 
262
  return ret;
 
263
}
 
264
 
 
265
void MagnatuneService::EnsureMenuCreated() {
 
266
  if (context_menu_)
 
267
    return;
 
268
 
 
269
  context_menu_ = new QMenu;
 
270
 
 
271
  context_menu_->addActions(GetPlaylistActions());
 
272
  download_ = context_menu_->addAction(
 
273
      IconLoader::Load("download"), tr("Download this album"), this, SLOT(Download()));
 
274
  context_menu_->addSeparator();
 
275
  context_menu_->addAction(IconLoader::Load("download"), tr("Open %1 in browser").arg("magnatune.com"), this, SLOT(Homepage()));
 
276
  context_menu_->addAction(IconLoader::Load("view-refresh"), tr("Refresh catalogue"), this, SLOT(ReloadDatabase()));
 
277
  QAction* config_action = context_menu_->addAction(IconLoader::Load("configure"), tr("Configure Magnatune..."), this, SLOT(ShowConfig()));
 
278
 
 
279
  library_filter_ = new LibraryFilterWidget(0);
 
280
  library_filter_->SetSettingsGroup(kSettingsGroup);
 
281
  library_filter_->SetLibraryModel(library_model_);
 
282
  library_filter_->SetFilterHint(tr("Search Magnatune"));
 
283
  library_filter_->SetAgeFilterEnabled(false);
 
284
  library_filter_->AddMenuAction(config_action);
 
285
 
 
286
  context_menu_->addSeparator();
 
287
  context_menu_->addMenu(library_filter_->menu());
 
288
}
 
289
 
 
290
void MagnatuneService::ShowContextMenu(const QModelIndex& index, const QPoint& global_pos) {
 
291
  EnsureMenuCreated();
 
292
 
 
293
  if (index.model() == library_sort_model_)
 
294
    context_item_ = index;
 
295
  else
 
296
    context_item_ = QModelIndex();
 
297
 
 
298
  GetAppendToPlaylistAction()->setEnabled(context_item_.isValid());
 
299
  GetReplacePlaylistAction()->setEnabled(context_item_.isValid());
 
300
  GetOpenInNewPlaylistAction()->setEnabled(context_item_.isValid());
 
301
  download_->setEnabled(context_item_.isValid() && membership_ == Membership_Download);
 
302
  context_menu_->popup(global_pos);
 
303
}
 
304
 
 
305
QModelIndex MagnatuneService::GetCurrentIndex() {
 
306
  return context_item_;
 
307
}
 
308
 
 
309
void MagnatuneService::Homepage() {
 
310
  QDesktopServices::openUrl(QUrl(kHomepage));
 
311
}
 
312
 
 
313
QUrl MagnatuneService::ModifyUrl(const QUrl& url) const {
 
314
  QUrl ret(url);
 
315
  ret.setScheme("http");
 
316
 
 
317
  switch(membership_) {
 
318
    case Membership_None:
 
319
      return ret; // Use the URL as-is
 
320
 
 
321
    // Otherwise add the hostname
 
322
    case Membership_Streaming:
 
323
      ret.setHost(kStreamingHostname);
 
324
      break;
 
325
    case Membership_Download:
 
326
      ret.setHost(kDownloadHostname);
 
327
      break;
 
328
  }
 
329
 
 
330
  // Add the credentials
 
331
  ret.setUserName(username_);
 
332
  ret.setPassword(password_);
 
333
 
 
334
  // And remove the commercial
 
335
  QString path = ret.path();
 
336
  path.insert(path.lastIndexOf('.'), "_nospeech");
 
337
  ret.setPath(path);
 
338
 
 
339
  return ret;
 
340
}
 
341
 
 
342
void MagnatuneService::ShowConfig() {
 
343
  emit OpenSettingsAtPage(SettingsDialog::Page_Magnatune);
 
344
}
 
345
 
 
346
void MagnatuneService::Download() {
 
347
  QModelIndex index = library_sort_model_->mapToSource(context_item_);
 
348
  SongList songs = library_model_->GetChildSongs(index);
 
349
 
 
350
  MagnatuneDownloadDialog* dialog = new MagnatuneDownloadDialog(this, 0);
 
351
  dialog->setAttribute(Qt::WA_DeleteOnClose);
 
352
  dialog->Show(songs);
 
353
 
 
354
  connect(dialog, SIGNAL(Finished(QStringList)), SIGNAL(DownloadFinished(QStringList)));
 
355
}
 
356
 
 
357
QWidget* MagnatuneService::HeaderWidget() const {
 
358
  const_cast<MagnatuneService*>(this)->EnsureMenuCreated();
 
359
  return library_filter_;
 
360
}