Merge lp:~max-linke/mixxx/chromaprint into lp:~mixxxdevelopers/mixxx/trunk
- chromaprint
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 3397 |
Proposed branch: | lp:~max-linke/mixxx/chromaprint |
Merge into: | lp:~mixxxdevelopers/mixxx/trunk |
Diff against target: |
2462 lines (+2023/-87) 24 files modified
mixxx/build/depends.py (+23/-1) mixxx/build/qtcreator/mixxx.pro (+1/-0) mixxx/src/dlgtagfetcher.cpp (+159/-0) mixxx/src/dlgtagfetcher.h (+57/-0) mixxx/src/dlgtagfetcher.ui (+441/-0) mixxx/src/dlgtrackinfo.cpp (+22/-9) mixxx/src/dlgtrackinfo.h (+5/-2) mixxx/src/dlgtrackinfo.ui (+78/-71) mixxx/src/musicbrainz/acoustidclient.cpp (+109/-0) mixxx/src/musicbrainz/acoustidclient.h (+67/-0) mixxx/src/musicbrainz/chromaprinter.cpp (+88/-0) mixxx/src/musicbrainz/chromaprinter.h (+25/-0) mixxx/src/musicbrainz/crc.c (+133/-0) mixxx/src/musicbrainz/crc.h (+83/-0) mixxx/src/musicbrainz/gzip.cpp (+49/-0) mixxx/src/musicbrainz/gzip.h (+12/-0) mixxx/src/musicbrainz/musicbrainzclient.cpp (+180/-0) mixxx/src/musicbrainz/musicbrainzclient.h (+130/-0) mixxx/src/musicbrainz/tagfetcher.cpp (+121/-0) mixxx/src/musicbrainz/tagfetcher.h (+58/-0) mixxx/src/network.cpp (+73/-0) mixxx/src/network.h (+49/-0) mixxx/src/widget/wtracktableview.cpp (+53/-4) mixxx/src/widget/wtracktableview.h (+7/-0) |
To merge this branch: | bzr merge lp:~max-linke/mixxx/chromaprint |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
RJ Skerry-Ryan | Approve | ||
Review via email: mp+163406@code.launchpad.net |
Commit message
Description of the change
Hi
I decided to split up my library_features branch in smaller branches. In here are all changes relevant to fingerprinting and retreiving tags from MusicBrainz
RJ Skerry-Ryan (rryan) wrote : | # |
RJ Skerry-Ryan (rryan) wrote : | # |
Doesn't MB have more data than artist/
When you set them, what about checking whether the existing data is blank or the replacement data is blank and keeping the old one if the new one is blank?
Max Linke (max-linke) wrote : | # |
'reload from MB' is grayed out if more then one song is selected. The tagfetcher class is able to handle to fetch tags from several songs at once but I haven't found a good way to display several tracks+results to the user and by this I follow the same logic as 'properties' does.
I didn't check to keep the old one if the new data is blank but I honestly also never encountered that with any track I own. I sure I can write some code that checks for this but I don't know how I'm supposed to test if it works.
RJ Skerry-Ryan (rryan) wrote : | # |
Looks good -- thanks for the feature Max!
Preview Diff
1 | === modified file 'mixxx/build/depends.py' |
2 | --- mixxx/build/depends.py 2013-05-21 03:02:42 +0000 |
3 | +++ mixxx/build/depends.py 2013-05-22 07:42:27 +0000 |
4 | @@ -400,6 +400,17 @@ |
5 | if build.platform_is_windows and build.static_dependencies: |
6 | build.env.Append(CPPDEFINES = 'TAGLIB_STATIC') |
7 | |
8 | +class Chromaprint(Dependence): |
9 | + def configure(self, build, conf): |
10 | + if not conf.CheckLib(['chromaprint', 'libchromaprint', 'chromaprint_p', 'libchromaprint_p']): |
11 | + raise Exception("Could not find libchromaprint or its development headers.") |
12 | + if build.platform_is_windows and build.static_dependencies: |
13 | + build.env.Append(CPPDEFINES = 'CHROMAPRINT_NODLL') |
14 | + |
15 | + # On Windows, we link chromaprint with FFTW3. |
16 | + if not conf.CheckLib(['fftw', 'libfftw', 'fftw3', 'libfftw3']): |
17 | + raise Exception("Could not find fftw3 or its development headers.") |
18 | + |
19 | class ProtoBuf(Dependence): |
20 | def configure(self, build, conf): |
21 | libs = ['libprotobuf-lite', 'protobuf-lite', 'libprotobuf', 'protobuf'] |
22 | @@ -447,6 +458,7 @@ |
23 | "dlgabout.cpp", |
24 | "dlgprefeq.cpp", |
25 | "dlgprefcrossfader.cpp", |
26 | + "dlgtagfetcher.cpp", |
27 | "dlgtrackinfo.cpp", |
28 | "dlgprepare.cpp", |
29 | "dlgautodj.cpp", |
30 | @@ -545,6 +557,14 @@ |
31 | |
32 | "mathstuff.cpp", |
33 | |
34 | + "network.cpp", |
35 | + "musicbrainz/tagfetcher.cpp", |
36 | + "musicbrainz/gzip.cpp", |
37 | + "musicbrainz/crc.c", |
38 | + "musicbrainz/acoustidclient.cpp", |
39 | + "musicbrainz/chromaprinter.cpp", |
40 | + "musicbrainz/musicbrainzclient.cpp", |
41 | + |
42 | "rotary.cpp", |
43 | "widget/wtracktableview.cpp", |
44 | "widget/wtracktableviewheader.cpp", |
45 | @@ -756,6 +776,7 @@ |
46 | build.env.Uic4('dlgprefnovinyldlg.ui') |
47 | build.env.Uic4('dlgprefrecorddlg.ui') |
48 | build.env.Uic4('dlgaboutdlg.ui') |
49 | + build.env.Uic4('dlgtagfetcher.ui') |
50 | build.env.Uic4('dlgtrackinfo.ui') |
51 | build.env.Uic4('dlgprepare.ui') |
52 | build.env.Uic4('dlgautodj.ui') |
53 | @@ -910,7 +931,8 @@ |
54 | |
55 | def depends(self, build): |
56 | return [SoundTouch, ReplayGain, PortAudio, PortMIDI, Qt, |
57 | - FidLib, SndFile, FLAC, OggVorbis, OpenGL, TagLib, ProtoBuf] |
58 | + FidLib, SndFile, FLAC, OggVorbis, OpenGL, TagLib, ProtoBuf, |
59 | + Chromaprint] |
60 | |
61 | def post_dependency_check_configure(self, build, conf): |
62 | """Sets up additional things in the Environment that must happen |
63 | |
64 | === modified file 'mixxx/build/qtcreator/mixxx.pro' |
65 | --- mixxx/build/qtcreator/mixxx.pro 2013-05-20 02:18:33 +0000 |
66 | +++ mixxx/build/qtcreator/mixxx.pro 2013-05-22 07:42:27 +0000 |
67 | @@ -105,6 +105,7 @@ |
68 | $$UI_DIR/ui_dlgpreferencesdlg.h \ |
69 | $$UI_DIR/ui_dlgprefmidibindingsdlg.h \ |
70 | $$UI_DIR/ui_dlgprefplaylistdlg.h \ |
71 | + $$UI_DIR/ui_dlgtagfetcher.h \ |
72 | $$UI_DIR/ui_dlgprefrecorddlg.h \ |
73 | $$UI_DIR/ui_dlgprefsounddlg.h \ |
74 | $$UI_DIR/ui_dlgprefvinyldlg.h \ |
75 | |
76 | === added file 'mixxx/src/dlgtagfetcher.cpp' |
77 | --- mixxx/src/dlgtagfetcher.cpp 1970-01-01 00:00:00 +0000 |
78 | +++ mixxx/src/dlgtagfetcher.cpp 2013-05-22 07:42:27 +0000 |
79 | @@ -0,0 +1,159 @@ |
80 | +#include <QTreeWidget> |
81 | +#include <QDebug> |
82 | +#include <QDesktopServices> |
83 | +#include <QUrl> |
84 | + |
85 | +#include "dlgtagfetcher.h" |
86 | + |
87 | +DlgTagFetcher::DlgTagFetcher(QWidget *parent) |
88 | + : QWidget(parent), |
89 | + m_track(NULL), |
90 | + m_TagFetcher(parent) { |
91 | + setupUi(this); |
92 | + |
93 | + connect(btnApply, SIGNAL(clicked()), |
94 | + this, SLOT(apply())); |
95 | + connect(btnQuit, SIGNAL(clicked()), |
96 | + this, SLOT(quit())); |
97 | + connect(btnPrev, SIGNAL(clicked()), |
98 | + this, SIGNAL(previous())); |
99 | + connect(btnNext, SIGNAL(clicked()), |
100 | + this, SIGNAL(next())); |
101 | + connect(results, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), |
102 | + this, SLOT(resultSelected())); |
103 | + |
104 | + connect(&m_TagFetcher, SIGNAL(resultAvailable(const TrackPointer,const QList<TrackPointer>&)), |
105 | + this, SLOT(fetchTagFinished(const TrackPointer,const QList<TrackPointer>&))); |
106 | + connect(&m_TagFetcher, SIGNAL(fetchProgress(QString)), |
107 | + this, SLOT(fetchTagProgress(QString))); |
108 | + |
109 | + // Resize columns, this can't be set in the ui file |
110 | + results->setColumnWidth(0, 50); // Track column |
111 | + results->setColumnWidth(1, 50); // Year column |
112 | + results->setColumnWidth(2, 160); // Title column |
113 | + results->setColumnWidth(3, 160); // Artist column |
114 | + results->setColumnWidth(4, 160); // Album column |
115 | + |
116 | + progressLabel->setText(""); |
117 | +} |
118 | + |
119 | +DlgTagFetcher::~DlgTagFetcher() { |
120 | +} |
121 | + |
122 | +void DlgTagFetcher::init(const TrackPointer track) { |
123 | + results->clear(); |
124 | + m_track = track; |
125 | + m_data = Data(); |
126 | + m_TagFetcher.startFetch(m_track); |
127 | + updateStack(); |
128 | +} |
129 | + |
130 | +void DlgTagFetcher::apply() { |
131 | + int resultIndex = m_data.m_selectedResult; |
132 | + if (resultIndex > -1) { |
133 | + if (!m_data.m_results[resultIndex]->getAlbum().isEmpty()) { |
134 | + m_track->setAlbum(m_data.m_results[resultIndex]->getAlbum()); |
135 | + } |
136 | + if (!m_data.m_results[resultIndex]->getArtist().isEmpty()) { |
137 | + m_track->setArtist(m_data.m_results[resultIndex]->getArtist()); |
138 | + } |
139 | + if (!m_data.m_results[resultIndex]->getTitle().isEmpty()) { |
140 | + m_track->setTitle(m_data.m_results[resultIndex]->getTitle()); |
141 | + } |
142 | + if (!m_data.m_results[resultIndex]->getYear().isEmpty() && |
143 | + m_data.m_results[resultIndex]->getYear() != "0") { |
144 | + m_track->setYear(m_data.m_results[resultIndex]->getYear()); |
145 | + } |
146 | + if (!m_data.m_results[resultIndex]->getTrackNumber().isEmpty() && |
147 | + m_data.m_results[resultIndex]->getTrackNumber() != "0") { |
148 | + m_track->setTrackNumber(m_data.m_results[resultIndex]->getTrackNumber()); |
149 | + } |
150 | + } |
151 | +} |
152 | + |
153 | +void DlgTagFetcher::quit() { |
154 | + m_TagFetcher.cancel(); |
155 | + close(); |
156 | +} |
157 | + |
158 | +void DlgTagFetcher::fetchTagProgress(QString text) { |
159 | + loading->setText(text); |
160 | +} |
161 | + |
162 | +void DlgTagFetcher::fetchTagFinished(const TrackPointer track, |
163 | + const QList<TrackPointer>& tracks) { |
164 | + // check if the answer is for this track |
165 | + if (m_track->getLocation() != track->getLocation()) { |
166 | + return; |
167 | + } |
168 | + |
169 | + m_data.m_pending = false; |
170 | + m_data.m_results = tracks; |
171 | + // qDebug() << "number of results = " << tracks.size(); |
172 | + updateStack(); |
173 | +} |
174 | + |
175 | +void DlgTagFetcher::updateStack() { |
176 | + if (m_data.m_pending) { |
177 | + stack->setCurrentWidget(loading_page); |
178 | + return; |
179 | + } else if (m_data.m_results.isEmpty()) { |
180 | + stack->setCurrentWidget(error_page); |
181 | + return; |
182 | + } |
183 | + btnApply->setEnabled(true); |
184 | + stack->setCurrentWidget(results_page); |
185 | + |
186 | + results->clear(); |
187 | + |
188 | + addDivider(tr("Original tags"), results); |
189 | + addTrack(m_track, -1, results); |
190 | + |
191 | + addDivider(tr("Suggested tags"), results); |
192 | + |
193 | + int trackIndex = 0; |
194 | + foreach (const TrackPointer track, m_data.m_results) { |
195 | + addTrack(track, trackIndex++, results); |
196 | + } |
197 | + |
198 | + // Find the item that was selected last time |
199 | + for (int i=0 ; i<results->model()->rowCount() ; ++i) { |
200 | + const QModelIndex index = results->model()->index(i, 0); |
201 | + const QVariant id = index.data(Qt::UserRole); |
202 | + if (!id.isNull() && id.toInt() == m_data.m_selectedResult) { |
203 | + results->setCurrentIndex(index); |
204 | + break; |
205 | + } |
206 | + } |
207 | +} |
208 | + |
209 | +void DlgTagFetcher::addTrack(const TrackPointer track, int resultIndex, |
210 | + QTreeWidget* parent) const { |
211 | + QStringList values; |
212 | + values << track->getTrackNumber() << track->getYear() << track->getTitle() |
213 | + << track->getArtist() << track->getAlbum(); |
214 | + |
215 | + QTreeWidgetItem* item = new QTreeWidgetItem(parent, values); |
216 | + item->setData(0, Qt::UserRole, resultIndex); |
217 | + item->setData(0, Qt::TextAlignmentRole, Qt::AlignRight); |
218 | +} |
219 | + |
220 | +void DlgTagFetcher::addDivider(const QString& text, QTreeWidget* parent) const { |
221 | + QTreeWidgetItem* item = new QTreeWidgetItem(parent); |
222 | + item->setFirstColumnSpanned(true); |
223 | + item->setText(0, text); |
224 | + item->setFlags(Qt::NoItemFlags); |
225 | + item->setForeground(0, palette().color(QPalette::Disabled, QPalette::Text)); |
226 | + |
227 | + QFont bold_font(font()); |
228 | + bold_font.setBold(true); |
229 | + item->setFont(0, bold_font); |
230 | +} |
231 | + |
232 | +void DlgTagFetcher::resultSelected() { |
233 | + if (!results->currentItem()) |
234 | + return; |
235 | + |
236 | + const int resultIndex = results->currentItem()->data(0, Qt::UserRole).toInt(); |
237 | + m_data.m_selectedResult = resultIndex; |
238 | +} |
239 | |
240 | === added file 'mixxx/src/dlgtagfetcher.h' |
241 | --- mixxx/src/dlgtagfetcher.h 1970-01-01 00:00:00 +0000 |
242 | +++ mixxx/src/dlgtagfetcher.h 2013-05-22 07:42:27 +0000 |
243 | @@ -0,0 +1,57 @@ |
244 | +#ifndef DLGTAGFETCHER_H |
245 | +#define DLGTAGFETCHER_H |
246 | + |
247 | +#include <QWidget> |
248 | +#include "ui_dlgtagfetcher.h" |
249 | +#include "trackinfoobject.h" |
250 | +#include "musicbrainz/tagfetcher.h" |
251 | + |
252 | + |
253 | +class QTreeWidget; |
254 | + |
255 | + |
256 | +class DlgTagFetcher : public QWidget, public Ui::DlgTagFetcher { |
257 | + Q_OBJECT |
258 | + |
259 | + public: |
260 | + DlgTagFetcher(QWidget *parent); |
261 | + virtual ~DlgTagFetcher(); |
262 | + |
263 | + void init(const TrackPointer track); |
264 | + |
265 | + signals: |
266 | + void next(); |
267 | + void previous(); |
268 | + |
269 | + public slots: |
270 | + void fetchTagFinished(const TrackPointer,const QList<TrackPointer>& tracks); |
271 | + void resultSelected(); |
272 | + void fetchTagProgress(QString); |
273 | + |
274 | + private slots: |
275 | + void apply(); |
276 | + void quit(); |
277 | + |
278 | + private: |
279 | + |
280 | + void updateStack(); |
281 | + void addDivider(const QString& text, QTreeWidget* parent) const; |
282 | + void addTrack(const TrackPointer track, int resultIndex, |
283 | + QTreeWidget* parent) const; |
284 | + |
285 | + private: |
286 | + struct Data { |
287 | + Data() : m_pending(true), m_selectedResult(0) {} |
288 | + |
289 | + bool m_pending; |
290 | + int m_selectedResult; |
291 | + QList<TrackPointer> m_results; |
292 | + }; |
293 | + |
294 | + TrackPointer m_track; |
295 | + Data m_data; |
296 | + QString m_progress; |
297 | + TagFetcher m_TagFetcher; |
298 | +}; |
299 | + |
300 | +#endif // DLGTAGFETCHER_H |
301 | |
302 | === added file 'mixxx/src/dlgtagfetcher.ui' |
303 | --- mixxx/src/dlgtagfetcher.ui 1970-01-01 00:00:00 +0000 |
304 | +++ mixxx/src/dlgtagfetcher.ui 2013-05-22 07:42:27 +0000 |
305 | @@ -0,0 +1,441 @@ |
306 | +<?xml version="1.0" encoding="UTF-8"?> |
307 | +<ui version="4.0"> |
308 | + <class>DlgTagFetcher</class> |
309 | + <widget class="QWidget" name="DlgTagFetcher"> |
310 | + <property name="geometry"> |
311 | + <rect> |
312 | + <x>0</x> |
313 | + <y>0</y> |
314 | + <width>810</width> |
315 | + <height>393</height> |
316 | + </rect> |
317 | + </property> |
318 | + <property name="windowTitle"> |
319 | + <string>Dialog</string> |
320 | + </property> |
321 | + <widget class="QWidget" name="verticalLayoutWidget_3"> |
322 | + <property name="geometry"> |
323 | + <rect> |
324 | + <x>10</x> |
325 | + <y>10</y> |
326 | + <width>791</width> |
327 | + <height>371</height> |
328 | + </rect> |
329 | + </property> |
330 | + <layout class="QVBoxLayout" name="verticalLayout_3"> |
331 | + <item> |
332 | + <widget class="QStackedWidget" name="stack"> |
333 | + <property name="currentIndex"> |
334 | + <number>1</number> |
335 | + </property> |
336 | + <widget class="QWidget" name="results_page"> |
337 | + <widget class="QWidget" name="verticalLayoutWidget_2"> |
338 | + <property name="geometry"> |
339 | + <rect> |
340 | + <x>0</x> |
341 | + <y>0</y> |
342 | + <width>791</width> |
343 | + <height>331</height> |
344 | + </rect> |
345 | + </property> |
346 | + <layout class="QVBoxLayout" name="verticalLayout_2"> |
347 | + <item> |
348 | + <widget class="QLabel" name="label_3"> |
349 | + <property name="styleSheet"> |
350 | + <string notr="true">QLabel { font-weight: bold; }</string> |
351 | + </property> |
352 | + <property name="text"> |
353 | + <string>Select best possible match</string> |
354 | + </property> |
355 | + </widget> |
356 | + </item> |
357 | + <item> |
358 | + <widget class="QTreeWidget" name="results"> |
359 | + <property name="editTriggers"> |
360 | + <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> |
361 | + </property> |
362 | + <property name="rootIsDecorated"> |
363 | + <bool>false</bool> |
364 | + </property> |
365 | + <attribute name="headerDefaultSectionSize"> |
366 | + <number>150</number> |
367 | + </attribute> |
368 | + <attribute name="headerMinimumSectionSize"> |
369 | + <number>50</number> |
370 | + </attribute> |
371 | + <column> |
372 | + <property name="text"> |
373 | + <string>Track</string> |
374 | + </property> |
375 | + </column> |
376 | + <column> |
377 | + <property name="text"> |
378 | + <string>Year</string> |
379 | + </property> |
380 | + </column> |
381 | + <column> |
382 | + <property name="text"> |
383 | + <string>Title</string> |
384 | + </property> |
385 | + </column> |
386 | + <column> |
387 | + <property name="text"> |
388 | + <string>Artist</string> |
389 | + </property> |
390 | + </column> |
391 | + <column> |
392 | + <property name="text"> |
393 | + <string>Album</string> |
394 | + </property> |
395 | + </column> |
396 | + </widget> |
397 | + </item> |
398 | + </layout> |
399 | + </widget> |
400 | + </widget> |
401 | + <widget class="QWidget" name="loading_page"> |
402 | + <widget class="QWidget" name="verticalLayoutWidget_4"> |
403 | + <property name="geometry"> |
404 | + <rect> |
405 | + <x>-1</x> |
406 | + <y>-1</y> |
407 | + <width>791</width> |
408 | + <height>341</height> |
409 | + </rect> |
410 | + </property> |
411 | + <layout class="QVBoxLayout" name="verticalLayout_4"> |
412 | + <item> |
413 | + <spacer name="verticalSpacer_3"> |
414 | + <property name="orientation"> |
415 | + <enum>Qt::Vertical</enum> |
416 | + </property> |
417 | + <property name="sizeHint" stdset="0"> |
418 | + <size> |
419 | + <width>20</width> |
420 | + <height>40</height> |
421 | + </size> |
422 | + </property> |
423 | + </spacer> |
424 | + </item> |
425 | + <item> |
426 | + <layout class="QHBoxLayout" name="horizontalLayout_3"> |
427 | + <item> |
428 | + <spacer name="horizontalSpacer_4"> |
429 | + <property name="orientation"> |
430 | + <enum>Qt::Horizontal</enum> |
431 | + </property> |
432 | + <property name="sizeHint" stdset="0"> |
433 | + <size> |
434 | + <width>40</width> |
435 | + <height>20</height> |
436 | + </size> |
437 | + </property> |
438 | + </spacer> |
439 | + </item> |
440 | + <item> |
441 | + <widget class="QLabel" name="message1"> |
442 | + <property name="text"> |
443 | + <string><html><head/><body><p><span style=" font-weight:600;">Fetching track data from MusicBrainz</span></p></body></html></string> |
444 | + </property> |
445 | + </widget> |
446 | + </item> |
447 | + <item> |
448 | + <spacer name="horizontalSpacer_5"> |
449 | + <property name="orientation"> |
450 | + <enum>Qt::Horizontal</enum> |
451 | + </property> |
452 | + <property name="sizeHint" stdset="0"> |
453 | + <size> |
454 | + <width>40</width> |
455 | + <height>20</height> |
456 | + </size> |
457 | + </property> |
458 | + </spacer> |
459 | + </item> |
460 | + </layout> |
461 | + </item> |
462 | + <item> |
463 | + <layout class="QHBoxLayout" name="horizontalLayout_4"> |
464 | + <item> |
465 | + <spacer name="horizontalSpacer_6"> |
466 | + <property name="orientation"> |
467 | + <enum>Qt::Horizontal</enum> |
468 | + </property> |
469 | + <property name="sizeHint" stdset="0"> |
470 | + <size> |
471 | + <width>40</width> |
472 | + <height>20</height> |
473 | + </size> |
474 | + </property> |
475 | + </spacer> |
476 | + </item> |
477 | + <item> |
478 | + <widget class="QLabel" name="status"> |
479 | + <property name="text"> |
480 | + <string><html><head/><body><p><span style=" font-weight:600;">Status:</span></p></body></html></string> |
481 | + </property> |
482 | + </widget> |
483 | + </item> |
484 | + <item> |
485 | + <widget class="QLabel" name="loading"> |
486 | + <property name="text"> |
487 | + <string/> |
488 | + </property> |
489 | + </widget> |
490 | + </item> |
491 | + <item> |
492 | + <spacer name="horizontalSpacer_7"> |
493 | + <property name="orientation"> |
494 | + <enum>Qt::Horizontal</enum> |
495 | + </property> |
496 | + <property name="sizeHint" stdset="0"> |
497 | + <size> |
498 | + <width>40</width> |
499 | + <height>20</height> |
500 | + </size> |
501 | + </property> |
502 | + </spacer> |
503 | + </item> |
504 | + </layout> |
505 | + </item> |
506 | + <item> |
507 | + <spacer name="verticalSpacer_4"> |
508 | + <property name="orientation"> |
509 | + <enum>Qt::Vertical</enum> |
510 | + </property> |
511 | + <property name="sizeHint" stdset="0"> |
512 | + <size> |
513 | + <width>20</width> |
514 | + <height>40</height> |
515 | + </size> |
516 | + </property> |
517 | + </spacer> |
518 | + </item> |
519 | + </layout> |
520 | + </widget> |
521 | + </widget> |
522 | + <widget class="QWidget" name="error_page"> |
523 | + <widget class="QWidget" name="verticalLayoutWidget"> |
524 | + <property name="geometry"> |
525 | + <rect> |
526 | + <x>0</x> |
527 | + <y>0</y> |
528 | + <width>791</width> |
529 | + <height>331</height> |
530 | + </rect> |
531 | + </property> |
532 | + <layout class="QVBoxLayout" name="verticalLayout"> |
533 | + <item> |
534 | + <spacer name="verticalSpacer"> |
535 | + <property name="orientation"> |
536 | + <enum>Qt::Vertical</enum> |
537 | + </property> |
538 | + <property name="sizeHint" stdset="0"> |
539 | + <size> |
540 | + <width>20</width> |
541 | + <height>40</height> |
542 | + </size> |
543 | + </property> |
544 | + </spacer> |
545 | + </item> |
546 | + <item> |
547 | + <widget class="QLabel" name="label"> |
548 | + <property name="text"> |
549 | + <string><html><head/><body><p align="center"><span style=" font-size:14pt; font-weight:600;">Sorry</span></p></body></html></string> |
550 | + </property> |
551 | + </widget> |
552 | + </item> |
553 | + <item> |
554 | + <widget class="QLabel" name="label_2"> |
555 | + <property name="text"> |
556 | + <string><html><head/><body><p align="center">Mixxx could not find this song</p></body></html></string> |
557 | + </property> |
558 | + </widget> |
559 | + </item> |
560 | + <item> |
561 | + <spacer name="verticalSpacer_2"> |
562 | + <property name="orientation"> |
563 | + <enum>Qt::Vertical</enum> |
564 | + </property> |
565 | + <property name="sizeHint" stdset="0"> |
566 | + <size> |
567 | + <width>20</width> |
568 | + <height>40</height> |
569 | + </size> |
570 | + </property> |
571 | + </spacer> |
572 | + </item> |
573 | + </layout> |
574 | + </widget> |
575 | + </widget> |
576 | + <widget class="QWidget" name="submit_page"> |
577 | + <widget class="QWidget" name="verticalLayoutWidget_5"> |
578 | + <property name="geometry"> |
579 | + <rect> |
580 | + <x>1</x> |
581 | + <y>0</y> |
582 | + <width>791</width> |
583 | + <height>331</height> |
584 | + </rect> |
585 | + </property> |
586 | + <layout class="QVBoxLayout" name="verticalLayout_5"> |
587 | + <item> |
588 | + <layout class="QHBoxLayout" name="horizontalLayout_2"> |
589 | + <item> |
590 | + <widget class="QLineEdit" name="apikey"> |
591 | + <property name="text"> |
592 | + <string/> |
593 | + </property> |
594 | + </widget> |
595 | + </item> |
596 | + <item> |
597 | + <widget class="QPushButton" name="btnApikey"> |
598 | + <property name="text"> |
599 | + <string>get API-Key</string> |
600 | + </property> |
601 | + </widget> |
602 | + </item> |
603 | + <item> |
604 | + <spacer name="horizontalSpacer_3"> |
605 | + <property name="orientation"> |
606 | + <enum>Qt::Horizontal</enum> |
607 | + </property> |
608 | + <property name="sizeHint" stdset="0"> |
609 | + <size> |
610 | + <width>40</width> |
611 | + <height>20</height> |
612 | + </size> |
613 | + </property> |
614 | + </spacer> |
615 | + </item> |
616 | + <item> |
617 | + <spacer name="horizontalSpacer_2"> |
618 | + <property name="orientation"> |
619 | + <enum>Qt::Horizontal</enum> |
620 | + </property> |
621 | + <property name="sizeHint" stdset="0"> |
622 | + <size> |
623 | + <width>40</width> |
624 | + <height>20</height> |
625 | + </size> |
626 | + </property> |
627 | + </spacer> |
628 | + </item> |
629 | + <item> |
630 | + <widget class="QPushButton" name="btnSubmit"> |
631 | + <property name="text"> |
632 | + <string>Submit</string> |
633 | + </property> |
634 | + </widget> |
635 | + </item> |
636 | + </layout> |
637 | + </item> |
638 | + <item> |
639 | + <widget class="QTreeWidget" name="submit_tree"> |
640 | + <column> |
641 | + <property name="text"> |
642 | + <string>Track</string> |
643 | + </property> |
644 | + </column> |
645 | + <column> |
646 | + <property name="text"> |
647 | + <string>Year</string> |
648 | + </property> |
649 | + </column> |
650 | + <column> |
651 | + <property name="text"> |
652 | + <string>New Column</string> |
653 | + </property> |
654 | + </column> |
655 | + <column> |
656 | + <property name="text"> |
657 | + <string>Artist</string> |
658 | + </property> |
659 | + </column> |
660 | + <column> |
661 | + <property name="text"> |
662 | + <string>Album</string> |
663 | + </property> |
664 | + </column> |
665 | + <item> |
666 | + <property name="text"> |
667 | + <string>New Item</string> |
668 | + </property> |
669 | + <property name="text"> |
670 | + <string/> |
671 | + </property> |
672 | + <property name="text"> |
673 | + <string/> |
674 | + </property> |
675 | + <property name="text"> |
676 | + <string/> |
677 | + </property> |
678 | + <property name="text"> |
679 | + <string/> |
680 | + </property> |
681 | + </item> |
682 | + </widget> |
683 | + </item> |
684 | + <item> |
685 | + <widget class="QLabel" name="progressLabel"> |
686 | + <property name="text"> |
687 | + <string>TextLabel</string> |
688 | + </property> |
689 | + </widget> |
690 | + </item> |
691 | + </layout> |
692 | + </widget> |
693 | + </widget> |
694 | + </widget> |
695 | + </item> |
696 | + <item> |
697 | + <layout class="QHBoxLayout" name="horizontalLayout"> |
698 | + <item> |
699 | + <widget class="QPushButton" name="btnPrev"> |
700 | + <property name="text"> |
701 | + <string>Prev</string> |
702 | + </property> |
703 | + </widget> |
704 | + </item> |
705 | + <item> |
706 | + <widget class="QPushButton" name="btnNext"> |
707 | + <property name="text"> |
708 | + <string>Next</string> |
709 | + </property> |
710 | + </widget> |
711 | + </item> |
712 | + <item> |
713 | + <spacer name="horizontalSpacer"> |
714 | + <property name="orientation"> |
715 | + <enum>Qt::Horizontal</enum> |
716 | + </property> |
717 | + <property name="sizeHint" stdset="0"> |
718 | + <size> |
719 | + <width>40</width> |
720 | + <height>20</height> |
721 | + </size> |
722 | + </property> |
723 | + </spacer> |
724 | + </item> |
725 | + <item> |
726 | + <widget class="QPushButton" name="btnApply"> |
727 | + <property name="text"> |
728 | + <string>Apply</string> |
729 | + </property> |
730 | + </widget> |
731 | + </item> |
732 | + <item> |
733 | + <widget class="QPushButton" name="btnQuit"> |
734 | + <property name="text"> |
735 | + <string>Close</string> |
736 | + </property> |
737 | + </widget> |
738 | + </item> |
739 | + </layout> |
740 | + </item> |
741 | + </layout> |
742 | + </widget> |
743 | + </widget> |
744 | + <resources/> |
745 | + <connections/> |
746 | +</ui> |
747 | |
748 | === modified file 'mixxx/src/dlgtrackinfo.cpp' |
749 | --- mixxx/src/dlgtrackinfo.cpp 2012-05-08 16:10:02 +0000 |
750 | +++ mixxx/src/dlgtrackinfo.cpp 2013-05-22 07:42:27 +0000 |
751 | @@ -7,10 +7,20 @@ |
752 | #include "library/dao/cue.h" |
753 | #include "trackinfoobject.h" |
754 | |
755 | -DlgTrackInfo::DlgTrackInfo(QWidget* parent) : |
756 | - QDialog(parent), |
757 | - m_pLoadedTrack() { |
758 | - |
759 | +DlgTrackInfo::DlgTrackInfo(QWidget* parent, |
760 | + DlgTagFetcher& DlgTagFetcher) |
761 | + : QDialog(parent), |
762 | + m_pLoadedTrack(NULL), |
763 | + m_DlgTagFetcher(DlgTagFetcher) { |
764 | + init(); |
765 | +} |
766 | + |
767 | +DlgTrackInfo::~DlgTrackInfo() { |
768 | + unloadTrack(false); |
769 | + qDebug() << "~DlgTrackInfo()"; |
770 | +} |
771 | + |
772 | +void DlgTrackInfo::init(){ |
773 | setupUi(this); |
774 | |
775 | cueTable->hideColumn(0); |
776 | @@ -24,6 +34,9 @@ |
777 | connect(btnCancel, SIGNAL(clicked()), |
778 | this, SLOT(cancel())); |
779 | |
780 | + connect(btnFetchTag, SIGNAL(clicked()), |
781 | + this, SLOT(fetchTag())); |
782 | + |
783 | connect(bpmDouble, SIGNAL(clicked()), |
784 | this, SLOT(slotBpmDouble())); |
785 | connect(bpmHalve, SIGNAL(clicked()), |
786 | @@ -43,11 +56,6 @@ |
787 | } |
788 | } |
789 | |
790 | -DlgTrackInfo::~DlgTrackInfo() { |
791 | - unloadTrack(false); |
792 | - qDebug() << "~DlgTrackInfo()"; |
793 | -} |
794 | - |
795 | void DlgTrackInfo::apply() { |
796 | unloadTrack(true); |
797 | accept(); |
798 | @@ -320,3 +328,8 @@ |
799 | populateFields(pTrack); |
800 | } |
801 | } |
802 | + |
803 | +void DlgTrackInfo::fetchTag() { |
804 | + m_DlgTagFetcher.init(m_pLoadedTrack); |
805 | + m_DlgTagFetcher.show(); |
806 | +} |
807 | |
808 | === modified file 'mixxx/src/dlgtrackinfo.h' |
809 | --- mixxx/src/dlgtrackinfo.h 2013-05-14 18:09:35 +0000 |
810 | +++ mixxx/src/dlgtrackinfo.h 2013-05-22 07:42:27 +0000 |
811 | @@ -8,8 +8,8 @@ |
812 | #include <QList> |
813 | |
814 | #include "ui_dlgtrackinfo.h" |
815 | - |
816 | #include "trackinfoobject.h" |
817 | +#include "dlgtagfetcher.h" |
818 | |
819 | /** Minimum allowed Beat per minute (BPM) */ |
820 | const int minBPM = 30; |
821 | @@ -25,7 +25,7 @@ |
822 | class DlgTrackInfo : public QDialog, public Ui::DlgTrackInfo { |
823 | Q_OBJECT |
824 | public: |
825 | - DlgTrackInfo(QWidget* parent); |
826 | + DlgTrackInfo(QWidget* parent, DlgTagFetcher& DlgTagFetcher); |
827 | virtual ~DlgTrackInfo(); |
828 | |
829 | public slots: |
830 | @@ -43,6 +43,7 @@ |
831 | void apply(); |
832 | void cancel(); |
833 | void trackUpdated(); |
834 | + void fetchTag(); |
835 | |
836 | void cueActivate(); |
837 | void cueDelete(); |
838 | @@ -58,6 +59,7 @@ |
839 | void populateCues(TrackPointer pTrack); |
840 | void unloadTrack(bool save); |
841 | void clear(); |
842 | + void init(); |
843 | |
844 | QHash<int, Cue*> m_cueMap; |
845 | TrackPointer m_pLoadedTrack; |
846 | @@ -66,6 +68,7 @@ |
847 | QTime m_bpmTapTimer; |
848 | |
849 | QMutex m_mutex; |
850 | + DlgTagFetcher& m_DlgTagFetcher; |
851 | |
852 | }; |
853 | |
854 | |
855 | === modified file 'mixxx/src/dlgtrackinfo.ui' |
856 | --- mixxx/src/dlgtrackinfo.ui 2012-05-08 16:10:02 +0000 |
857 | +++ mixxx/src/dlgtrackinfo.ui 2013-05-22 07:42:27 +0000 |
858 | @@ -61,6 +61,13 @@ |
859 | <item row="1" column="1" colspan="3"> |
860 | <widget class="QLineEdit" name="txtArtist"/> |
861 | </item> |
862 | + <item row="8" column="0"> |
863 | + <widget class="QLabel" name="label_7"> |
864 | + <property name="text"> |
865 | + <string>Duration:</string> |
866 | + </property> |
867 | + </widget> |
868 | + </item> |
869 | <item row="2" column="0"> |
870 | <widget class="QLabel" name="label_6"> |
871 | <property name="text"> |
872 | @@ -68,25 +75,12 @@ |
873 | </property> |
874 | </widget> |
875 | </item> |
876 | - <item row="2" column="1" colspan="3"> |
877 | - <widget class="QLineEdit" name="txtAlbum"/> |
878 | - </item> |
879 | - <item row="3" column="0"> |
880 | - <widget class="QLabel" name="label_9"> |
881 | - <property name="text"> |
882 | - <string>Genre:</string> |
883 | - </property> |
884 | - </widget> |
885 | - </item> |
886 | - <item row="4" column="0"> |
887 | - <widget class="QLabel" name="label_11"> |
888 | - <property name="text"> |
889 | - <string>Composer:</string> |
890 | - </property> |
891 | - </widget> |
892 | - </item> |
893 | - <item row="4" column="1" colspan="3"> |
894 | - <widget class="QLineEdit" name="txtComposer"/> |
895 | + <item row="6" column="0"> |
896 | + <widget class="QLabel" name="label_12"> |
897 | + <property name="text"> |
898 | + <string>Key</string> |
899 | + </property> |
900 | + </widget> |
901 | </item> |
902 | <item row="3" column="1" colspan="3"> |
903 | <widget class="QLineEdit" name="txtGenre"/> |
904 | @@ -98,33 +92,30 @@ |
905 | </property> |
906 | </widget> |
907 | </item> |
908 | - <item row="5" column="3"> |
909 | - <widget class="QLineEdit" name="txtTrackNumber"/> |
910 | - </item> |
911 | - <item row="5" column="1"> |
912 | - <widget class="QLineEdit" name="txtYear"/> |
913 | - </item> |
914 | - <item row="5" column="0"> |
915 | - <widget class="QLabel" name="label_8"> |
916 | - <property name="text"> |
917 | - <string>Year</string> |
918 | - </property> |
919 | - </widget> |
920 | - </item> |
921 | - <item row="6" column="1"> |
922 | - <widget class="QLineEdit" name="txtKey"/> |
923 | - </item> |
924 | - <item row="6" column="0"> |
925 | - <widget class="QLabel" name="label_12"> |
926 | - <property name="text"> |
927 | - <string>Key</string> |
928 | - </property> |
929 | - </widget> |
930 | - </item> |
931 | - <item row="8" column="0"> |
932 | - <widget class="QLabel" name="label_7"> |
933 | - <property name="text"> |
934 | - <string>Duration:</string> |
935 | + <item row="9" column="3"> |
936 | + <widget class="QLabel" name="txtBitrate"> |
937 | + <property name="text"> |
938 | + <string/> |
939 | + </property> |
940 | + <property name="textInteractionFlags"> |
941 | + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> |
942 | + </property> |
943 | + </widget> |
944 | + </item> |
945 | + <item row="12" column="0" colspan="4"> |
946 | + <widget class="Line" name="line_3"> |
947 | + <property name="orientation"> |
948 | + <enum>Qt::Horizontal</enum> |
949 | + </property> |
950 | + </widget> |
951 | + </item> |
952 | + <item row="11" column="0"> |
953 | + <widget class="QLabel" name="label_5"> |
954 | + <property name="text"> |
955 | + <string>Location:</string> |
956 | + </property> |
957 | + <property name="alignment"> |
958 | + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> |
959 | </property> |
960 | </widget> |
961 | </item> |
962 | @@ -155,6 +146,16 @@ |
963 | </property> |
964 | </widget> |
965 | </item> |
966 | + <item row="5" column="0"> |
967 | + <widget class="QLabel" name="label_8"> |
968 | + <property name="text"> |
969 | + <string>Year</string> |
970 | + </property> |
971 | + </widget> |
972 | + </item> |
973 | + <item row="6" column="1"> |
974 | + <widget class="QLineEdit" name="txtKey"/> |
975 | + </item> |
976 | <item row="9" column="0"> |
977 | <widget class="QLabel" name="label_13"> |
978 | <property name="text"> |
979 | @@ -172,30 +173,29 @@ |
980 | </property> |
981 | </widget> |
982 | </item> |
983 | - <item row="9" column="3"> |
984 | - <widget class="QLabel" name="txtBitrate"> |
985 | - <property name="text"> |
986 | - <string/> |
987 | - </property> |
988 | - <property name="textInteractionFlags"> |
989 | - <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> |
990 | - </property> |
991 | - </widget> |
992 | - </item> |
993 | - <item row="12" column="0" colspan="4"> |
994 | - <widget class="Line" name="line_3"> |
995 | - <property name="orientation"> |
996 | - <enum>Qt::Horizontal</enum> |
997 | - </property> |
998 | - </widget> |
999 | - </item> |
1000 | - <item row="11" column="0"> |
1001 | - <widget class="QLabel" name="label_5"> |
1002 | - <property name="text"> |
1003 | - <string>Location:</string> |
1004 | - </property> |
1005 | - <property name="alignment"> |
1006 | - <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> |
1007 | + <item row="4" column="0"> |
1008 | + <widget class="QLabel" name="label_11"> |
1009 | + <property name="text"> |
1010 | + <string>Composer:</string> |
1011 | + </property> |
1012 | + </widget> |
1013 | + </item> |
1014 | + <item row="4" column="1" colspan="3"> |
1015 | + <widget class="QLineEdit" name="txtComposer"/> |
1016 | + </item> |
1017 | + <item row="5" column="3"> |
1018 | + <widget class="QLineEdit" name="txtTrackNumber"/> |
1019 | + </item> |
1020 | + <item row="5" column="1"> |
1021 | + <widget class="QLineEdit" name="txtYear"/> |
1022 | + </item> |
1023 | + <item row="2" column="1" colspan="3"> |
1024 | + <widget class="QLineEdit" name="txtAlbum"/> |
1025 | + </item> |
1026 | + <item row="3" column="0"> |
1027 | + <widget class="QLabel" name="label_9"> |
1028 | + <property name="text"> |
1029 | + <string>Genre:</string> |
1030 | </property> |
1031 | </widget> |
1032 | </item> |
1033 | @@ -212,7 +212,7 @@ |
1034 | </property> |
1035 | </widget> |
1036 | </item> |
1037 | - <item row="13" column="1" colspan="3"> |
1038 | + <item row="14" column="1" colspan="3"> |
1039 | <widget class="QPushButton" name="btnReloadFromFile"> |
1040 | <property name="text"> |
1041 | <string>Reload track metadata from file.</string> |
1042 | @@ -275,6 +275,13 @@ |
1043 | </property> |
1044 | </widget> |
1045 | </item> |
1046 | + <item row="13" column="1" colspan="3"> |
1047 | + <widget class="QPushButton" name="btnFetchTag"> |
1048 | + <property name="text"> |
1049 | + <string>Reload track metadata from musicBrainz</string> |
1050 | + </property> |
1051 | + </widget> |
1052 | + </item> |
1053 | </layout> |
1054 | </item> |
1055 | <item> |
1056 | |
1057 | === added directory 'mixxx/src/musicbrainz' |
1058 | === added file 'mixxx/src/musicbrainz/acoustidclient.cpp' |
1059 | --- mixxx/src/musicbrainz/acoustidclient.cpp 1970-01-01 00:00:00 +0000 |
1060 | +++ mixxx/src/musicbrainz/acoustidclient.cpp 2013-05-22 07:42:27 +0000 |
1061 | @@ -0,0 +1,109 @@ |
1062 | +/***************************************************************************** |
1063 | + * Copyright © 2012 John Maguire <john.maguire@gmail.com> * |
1064 | + * David Sansome <me@davidsansome.com> * |
1065 | + * This work is free. You can redistribute it and/or modify it under the * |
1066 | + * terms of the Do What The Fuck You Want To Public License, Version 2, * |
1067 | + * as published by Sam Hocevar. * |
1068 | + * See http://www.wtfpl.net/ for more details. * |
1069 | + *****************************************************************************/ |
1070 | + |
1071 | +#include <QCoreApplication> |
1072 | +#include <QNetworkReply> |
1073 | +#include <QXmlStreamReader> |
1074 | + |
1075 | +#include "acoustidclient.h" |
1076 | +#include "gzip.h" |
1077 | +#include "network.h" |
1078 | + |
1079 | +// see API-KEY site here http://acoustid.org/application/496 |
1080 | +// I registered the KEY for version 1.12 -- kain88 (may 2013) |
1081 | +const QString CLIENT_APIKEY = "czKxnkyO"; |
1082 | +const QString CLIENT_NAME = "Mixxx1.12"; |
1083 | +const QString ACOUSTID_URL = "http://api.acoustid.org/v2/lookup"; |
1084 | +const int AcoustidClient::m_DefaultTimeout = 5000; // msec |
1085 | + |
1086 | +AcoustidClient::AcoustidClient(QObject* parent) |
1087 | + : QObject(parent), |
1088 | + m_network(this), |
1089 | + m_timeouts(m_DefaultTimeout, this) { |
1090 | +} |
1091 | + |
1092 | +void AcoustidClient::setTimeout(int msec) { |
1093 | + m_timeouts.setTimeout(msec); |
1094 | +} |
1095 | + |
1096 | +void AcoustidClient::start(int id, const QString& fingerprint, int duration) { |
1097 | + QUrl url; |
1098 | + url.addQueryItem("format", "xml"); |
1099 | + url.addQueryItem("client", CLIENT_APIKEY); |
1100 | + url.addQueryItem("duration", QString::number(duration)); |
1101 | + url.addQueryItem("meta", "recordingids"); |
1102 | + url.addQueryItem("fingerprint", fingerprint); |
1103 | + |
1104 | + QNetworkRequest req(QUrl::fromEncoded(ACOUSTID_URL.toAscii())); |
1105 | + req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); |
1106 | + req.setRawHeader("Content-Encoding", "gzip"); |
1107 | + req.setRawHeader("User-Agent", CLIENT_NAME.toAscii()); |
1108 | + |
1109 | + QNetworkReply* reply = m_network.post(req, gzipCompress(url.encodedQuery())); |
1110 | + connect(reply, SIGNAL(finished()), SLOT(requestFinished())); |
1111 | + m_requests[reply] = id; |
1112 | + |
1113 | + m_timeouts.addReply(reply); |
1114 | +} |
1115 | + |
1116 | +void AcoustidClient::cancel(int id) { |
1117 | + QNetworkReply* reply = m_requests.key(id); |
1118 | + m_requests.remove(reply); |
1119 | + delete reply; |
1120 | +} |
1121 | + |
1122 | +void AcoustidClient::cancelAll() { |
1123 | + qDeleteAll(m_requests.keys()); |
1124 | + m_requests.clear(); |
1125 | +} |
1126 | + |
1127 | +void AcoustidClient::requestFinished() { |
1128 | + QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender()); |
1129 | + if (!reply) |
1130 | + return; |
1131 | + |
1132 | + reply->deleteLater(); |
1133 | + if (!m_requests.contains(reply)) |
1134 | + return; |
1135 | + |
1136 | + int id = m_requests.take(reply); |
1137 | + |
1138 | + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { |
1139 | + emit finished(id, QString()); |
1140 | + return; |
1141 | + } |
1142 | + |
1143 | + QXmlStreamReader reader(reply); |
1144 | + QString ID; |
1145 | + while (!reader.atEnd()) { |
1146 | + if (reader.readNext() == QXmlStreamReader::StartElement |
1147 | + && reader.name()== "results") { |
1148 | + ID = parseResult(reader); |
1149 | + } |
1150 | + } |
1151 | + |
1152 | + emit finished(id, ID); |
1153 | +} |
1154 | + |
1155 | +QString AcoustidClient::parseResult(QXmlStreamReader& reader){ |
1156 | + |
1157 | + while (!reader.atEnd()) { |
1158 | + QXmlStreamReader::TokenType type = reader.readNext(); |
1159 | + if (type== QXmlStreamReader::StartElement) { |
1160 | + QStringRef name = reader.name(); |
1161 | + if (name == "id") { |
1162 | + return reader.readElementText(); |
1163 | + } |
1164 | + } |
1165 | + if (type == QXmlStreamReader::EndElement && reader.name()=="result") { |
1166 | + break; |
1167 | + } |
1168 | + } |
1169 | + return QString(); |
1170 | +} |
1171 | |
1172 | === added file 'mixxx/src/musicbrainz/acoustidclient.h' |
1173 | --- mixxx/src/musicbrainz/acoustidclient.h 1970-01-01 00:00:00 +0000 |
1174 | +++ mixxx/src/musicbrainz/acoustidclient.h 2013-05-22 07:42:27 +0000 |
1175 | @@ -0,0 +1,67 @@ |
1176 | +/***************************************************************************** |
1177 | + * Copyright © 2012 John Maguire <john.maguire@gmail.com> * |
1178 | + * David Sansome <me@davidsansome.com> * |
1179 | + * This work is free. You can redistribute it and/or modify it under the * |
1180 | + * terms of the Do What The Fuck You Want To Public License, Version 2, * |
1181 | + * as published by Sam Hocevar. * |
1182 | + * See http://www.wtfpl.net/ for more details. * |
1183 | + *****************************************************************************/ |
1184 | + |
1185 | +#ifndef ACOUSTIDCLIENT_H |
1186 | +#define ACOUSTIDCLIENT_H |
1187 | + |
1188 | +#include <QMap> |
1189 | +#include <QObject> |
1190 | +#include <QtNetwork> |
1191 | + |
1192 | +#include "network.h" |
1193 | +#include "trackinfoobject.h" |
1194 | + |
1195 | +class QXmlStreamReader; |
1196 | + |
1197 | +class AcoustidClient : public QObject { |
1198 | + Q_OBJECT |
1199 | + |
1200 | + // Gets a MBID from a Chromaprint fingerprint. |
1201 | + // A fingerprint identifies one particular encoding of a song and is created |
1202 | + // by Fingerprinter. An MBID identifies the actual song and can be passed to |
1203 | + // Musicbrainz to get metadata. |
1204 | + // You can create one AcoustidClient and make multiple requests using it. |
1205 | + // IDs are provided by the caller when a request is started and included in |
1206 | + // the finished signal - they have no meaning to AcoustidClient. |
1207 | + |
1208 | + public: |
1209 | + AcoustidClient(QObject* parent = 0); |
1210 | + |
1211 | + // Network requests will be aborted after this interval. |
1212 | + void setTimeout(int msec); |
1213 | + |
1214 | + // Starts a request and returns immediately. Finished() will be emitted |
1215 | + // later with the same ID. |
1216 | + void start(int id, const QString& fingerprint, int duration); |
1217 | + |
1218 | + // Cancels the request with the given ID. Finished() will never be emitted |
1219 | + // for that ID. Does nothing if there is no request with the given ID. |
1220 | + void cancel(int id); |
1221 | + |
1222 | + // Cancels all requests. Finished() will never be emitted for any pending |
1223 | + // requests. |
1224 | + void cancelAll(); |
1225 | + |
1226 | + QString parseResult(QXmlStreamReader& reader); |
1227 | + |
1228 | + signals: |
1229 | + void finished(int id, const QString& mbid); |
1230 | + |
1231 | + private slots: |
1232 | + void requestFinished(); |
1233 | + |
1234 | + private: |
1235 | + static const int m_DefaultTimeout; |
1236 | + |
1237 | + QNetworkAccessManager m_network; |
1238 | + NetworkTimeouts m_timeouts; |
1239 | + QMap<QNetworkReply*, int> m_requests; |
1240 | +}; |
1241 | + |
1242 | +#endif // ACOUSTIDCLIENT_H |
1243 | |
1244 | === added file 'mixxx/src/musicbrainz/chromaprinter.cpp' |
1245 | --- mixxx/src/musicbrainz/chromaprinter.cpp 1970-01-01 00:00:00 +0000 |
1246 | +++ mixxx/src/musicbrainz/chromaprinter.cpp 2013-05-22 07:42:27 +0000 |
1247 | @@ -0,0 +1,88 @@ |
1248 | +#include <QtCore> |
1249 | +#include <chromaprint.h> |
1250 | + |
1251 | +#include "musicbrainz/chromaprinter.h" |
1252 | +#include "soundsourceproxy.h" |
1253 | +#include "defs.h" |
1254 | + |
1255 | +chromaprinter::chromaprinter(QObject* parent) |
1256 | + : QObject(parent){ |
1257 | +} |
1258 | + |
1259 | +QString chromaprinter::getFingerPrint(TrackPointer pTrack){ |
1260 | + SoundSourceProxy soundSource(pTrack); |
1261 | + return calcFingerPrint(soundSource); |
1262 | +} |
1263 | + |
1264 | +QString chromaprinter::getFingerPrint(QString location){ |
1265 | + SoundSourceProxy soundSource(location); |
1266 | + return calcFingerPrint(soundSource); |
1267 | +} |
1268 | + |
1269 | +QString chromaprinter::calcFingerPrint(SoundSourceProxy& soundSource){ |
1270 | + soundSource.open(); |
1271 | + m_SampleRate = soundSource.getSampleRate(); |
1272 | + unsigned int length = soundSource.length(); |
1273 | + if (m_SampleRate == 0 ){ |
1274 | + qDebug() << "Skipping invalid file:" << soundSource.getFilename(); |
1275 | + return QString(); |
1276 | + } |
1277 | + |
1278 | + // this is worth 2min of audio, multiply by 2 because we have 2 channels |
1279 | + // AcoustID only stores a fingerprint for the first two minutes of a song |
1280 | + // on their server so we need only a fingerprint of the first two minutes |
1281 | + // --kain88 July 2012 |
1282 | + m_NumSamples = 120*2*m_SampleRate; |
1283 | + // check that the song is actually longer then the amount of audio we use |
1284 | + if (m_NumSamples > length) { |
1285 | + m_NumSamples = length; |
1286 | + } |
1287 | + |
1288 | + SAMPLE *pData = new SAMPLE[m_NumSamples]; |
1289 | + QTime timerReadingFile; |
1290 | + timerReadingFile.start(); |
1291 | + int read = soundSource.read(m_NumSamples, pData); |
1292 | + |
1293 | + if (read!=m_NumSamples) { |
1294 | + qDebug() << "oh that's embarrasing I couldn't read the track"; |
1295 | + return QString(); |
1296 | + } |
1297 | + qDebug("reading file took: %d ms" , timerReadingFile.elapsed()); |
1298 | + |
1299 | + ChromaprintContext* ctx = chromaprint_new(CHROMAPRINT_ALGORITHM_DEFAULT); |
1300 | + // we have 2 channels in mixxx always |
1301 | + chromaprint_start(ctx, m_SampleRate, 2); |
1302 | + |
1303 | + QTime timerGeneratingFingerPrint; |
1304 | + timerGeneratingFingerPrint.start(); |
1305 | + int success = chromaprint_feed(ctx, pData, m_NumSamples); |
1306 | + if (!success) { |
1307 | + qDebug() << "could not generate fingerprint"; |
1308 | + return QString(); |
1309 | + } |
1310 | + chromaprint_finish(ctx); |
1311 | + |
1312 | + void* fprint = NULL; |
1313 | + int size = 0; |
1314 | + int ret = chromaprint_get_raw_fingerprint(ctx, &fprint, &size); |
1315 | + QByteArray fingerprint; |
1316 | + if (ret == 1) { |
1317 | + void* encoded = NULL; |
1318 | + int encoded_size = 0; |
1319 | + chromaprint_encode_fingerprint(fprint, size, |
1320 | + CHROMAPRINT_ALGORITHM_DEFAULT, |
1321 | + &encoded, |
1322 | + &encoded_size, 1); |
1323 | + |
1324 | + fingerprint.append(reinterpret_cast<char*>(encoded), encoded_size); |
1325 | + |
1326 | + chromaprint_dealloc(fprint); |
1327 | + chromaprint_dealloc(encoded); |
1328 | + } |
1329 | + chromaprint_free(ctx); |
1330 | + delete pData; |
1331 | + |
1332 | + qDebug("generating fingerprint took: %d ms" , timerGeneratingFingerPrint.elapsed()); |
1333 | + |
1334 | + return fingerprint; |
1335 | +} |
1336 | |
1337 | === added file 'mixxx/src/musicbrainz/chromaprinter.h' |
1338 | --- mixxx/src/musicbrainz/chromaprinter.h 1970-01-01 00:00:00 +0000 |
1339 | +++ mixxx/src/musicbrainz/chromaprinter.h 2013-05-22 07:42:27 +0000 |
1340 | @@ -0,0 +1,25 @@ |
1341 | +#ifndef CHROMAPRINTER_H |
1342 | +#define CHROMAPRINTER_H |
1343 | + |
1344 | +#include <QObject> |
1345 | + |
1346 | +#include "trackinfoobject.h" |
1347 | + |
1348 | +class SoundSourceProxy; |
1349 | + |
1350 | +class chromaprinter: public QObject { |
1351 | + Q_OBJECT |
1352 | + |
1353 | + public: |
1354 | + chromaprinter(QObject* parent=NULL); |
1355 | + QString getFingerPrint(TrackPointer pTrack); |
1356 | + QString getFingerPrint(QString location); |
1357 | + |
1358 | + private: |
1359 | + |
1360 | + QString calcFingerPrint(SoundSourceProxy& soundSource); |
1361 | + unsigned int m_NumSamples; |
1362 | + unsigned int m_SampleRate; |
1363 | +}; |
1364 | + |
1365 | +#endif //CHROMAPRINTER_H |
1366 | |
1367 | === added file 'mixxx/src/musicbrainz/crc.c' |
1368 | --- mixxx/src/musicbrainz/crc.c 1970-01-01 00:00:00 +0000 |
1369 | +++ mixxx/src/musicbrainz/crc.c 2013-05-22 07:42:27 +0000 |
1370 | @@ -0,0 +1,133 @@ |
1371 | +/** |
1372 | + * \file crc.c |
1373 | + * Functions and types for CRC checks. |
1374 | + * |
1375 | + * Generated on Sun Oct 24 10:58:31 2010, |
1376 | + * by pycrc v0.7.6, http://www.tty1.net/pycrc/ |
1377 | + * using the configuration: |
1378 | + * Width = 32 |
1379 | + * Poly = 0x04c11db7 |
1380 | + * XorIn = 0xffffffff |
1381 | + * ReflectIn = True |
1382 | + * XorOut = 0xffffffff |
1383 | + * ReflectOut = True |
1384 | + * Algorithm = table-driven |
1385 | + *****************************************************************************/ |
1386 | +#include "crc.h" |
1387 | +#include <stdint.h> |
1388 | +#include <stdlib.h> |
1389 | + |
1390 | +/** |
1391 | + * Static table used for the table_driven implementation. |
1392 | + *****************************************************************************/ |
1393 | +static const crc_t crc_table[256] = { |
1394 | + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, |
1395 | + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, |
1396 | + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, |
1397 | + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, |
1398 | + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, |
1399 | + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, |
1400 | + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, |
1401 | + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, |
1402 | + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, |
1403 | + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, |
1404 | + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, |
1405 | + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, |
1406 | + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, |
1407 | + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, |
1408 | + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, |
1409 | + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, |
1410 | + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, |
1411 | + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, |
1412 | + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, |
1413 | + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, |
1414 | + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, |
1415 | + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, |
1416 | + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, |
1417 | + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, |
1418 | + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, |
1419 | + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, |
1420 | + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, |
1421 | + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, |
1422 | + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, |
1423 | + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, |
1424 | + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, |
1425 | + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, |
1426 | + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, |
1427 | + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, |
1428 | + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, |
1429 | + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, |
1430 | + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, |
1431 | + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, |
1432 | + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, |
1433 | + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, |
1434 | + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, |
1435 | + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, |
1436 | + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, |
1437 | + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, |
1438 | + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, |
1439 | + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, |
1440 | + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, |
1441 | + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, |
1442 | + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, |
1443 | + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, |
1444 | + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, |
1445 | + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, |
1446 | + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, |
1447 | + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, |
1448 | + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, |
1449 | + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, |
1450 | + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, |
1451 | + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, |
1452 | + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, |
1453 | + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, |
1454 | + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, |
1455 | + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, |
1456 | + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, |
1457 | + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d |
1458 | +}; |
1459 | + |
1460 | +/** |
1461 | + * Reflect all bits of a \a data word of \a data_len bytes. |
1462 | + * |
1463 | + * \param data The data word to be reflected. |
1464 | + * \param data_len The width of \a data expressed in number of bits. |
1465 | + * \return The reflected data. |
1466 | + *****************************************************************************/ |
1467 | +crc_t crc_reflect(crc_t data, size_t data_len) |
1468 | +{ |
1469 | + unsigned int i; |
1470 | + crc_t ret; |
1471 | + |
1472 | + ret = data & 0x01; |
1473 | + for (i = 1; i < data_len; i++) { |
1474 | + data >>= 1; |
1475 | + ret = (ret << 1) | (data & 0x01); |
1476 | + } |
1477 | + return ret; |
1478 | +} |
1479 | + |
1480 | + |
1481 | +/** |
1482 | + * Update the crc value with new data. |
1483 | + * |
1484 | + * \param crc The current crc value. |
1485 | + * \param data Pointer to a buffer of \a data_len bytes. |
1486 | + * \param data_len Number of bytes in the \a data buffer. |
1487 | + * \return The updated crc value. |
1488 | + *****************************************************************************/ |
1489 | +crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len) |
1490 | +{ |
1491 | + unsigned int tbl_idx; |
1492 | + |
1493 | + while (data_len--) { |
1494 | + tbl_idx = (crc ^ *data) & 0xff; |
1495 | + crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffffffff; |
1496 | + |
1497 | + data++; |
1498 | + } |
1499 | + return crc & 0xffffffff; |
1500 | +} |
1501 | + |
1502 | + |
1503 | + |
1504 | |
1505 | === added file 'mixxx/src/musicbrainz/crc.h' |
1506 | --- mixxx/src/musicbrainz/crc.h 1970-01-01 00:00:00 +0000 |
1507 | +++ mixxx/src/musicbrainz/crc.h 2013-05-22 07:42:27 +0000 |
1508 | @@ -0,0 +1,83 @@ |
1509 | +/** |
1510 | + * \file crc.h |
1511 | + * Functions and types for CRC checks. |
1512 | + * |
1513 | + * Generated on Sun Oct 24 10:58:41 2010, |
1514 | + * by pycrc v0.7.6, http://www.tty1.net/pycrc/ |
1515 | + * using the configuration: |
1516 | + * Width = 32 |
1517 | + * Poly = 0x04c11db7 |
1518 | + * XorIn = 0xffffffff |
1519 | + * ReflectIn = True |
1520 | + * XorOut = 0xffffffff |
1521 | + * ReflectOut = True |
1522 | + * Algorithm = table-driven |
1523 | + *****************************************************************************/ |
1524 | +#ifndef __CRC_H__ |
1525 | +#define __CRC_H__ |
1526 | + |
1527 | +#include <stdint.h> |
1528 | +#include <stdlib.h> |
1529 | + |
1530 | +#ifdef __cplusplus |
1531 | +extern "C" { |
1532 | +#endif |
1533 | + |
1534 | + |
1535 | +/** |
1536 | + * The definition of the used algorithm. |
1537 | + *****************************************************************************/ |
1538 | +#define CRC_ALGO_TABLE_DRIVEN 1 |
1539 | + |
1540 | + |
1541 | +/** |
1542 | + * The type of the CRC values. |
1543 | + * |
1544 | + * This type must be big enough to contain at least 32 bits. |
1545 | + *****************************************************************************/ |
1546 | +typedef unsigned long crc_t; |
1547 | + |
1548 | + |
1549 | +/** |
1550 | + * Reflect all bits of a \a data word of \a data_len bytes. |
1551 | + * |
1552 | + * \param data The data word to be reflected. |
1553 | + * \param data_len The width of \a data expressed in number of bits. |
1554 | + * \return The reflected data. |
1555 | + *****************************************************************************/ |
1556 | +crc_t crc_reflect(crc_t data, size_t data_len); |
1557 | + |
1558 | + |
1559 | +/** |
1560 | + * Calculate the initial crc value. |
1561 | + * |
1562 | + * \return The initial crc value. |
1563 | + *****************************************************************************/ |
1564 | +#define crc_init() (0xffffffff) |
1565 | + |
1566 | + |
1567 | +/** |
1568 | + * Update the crc value with new data. |
1569 | + * |
1570 | + * \param crc The current crc value. |
1571 | + * \param data Pointer to a buffer of \a data_len bytes. |
1572 | + * \param data_len Number of bytes in the \a data buffer. |
1573 | + * \return The updated crc value. |
1574 | + *****************************************************************************/ |
1575 | +crc_t crc_update(crc_t crc, const unsigned char *data, size_t data_len); |
1576 | + |
1577 | + |
1578 | +/** |
1579 | + * Calculate the final crc value. |
1580 | + * |
1581 | + * \param crc The current crc value. |
1582 | + * \return The final crc value. |
1583 | + *****************************************************************************/ |
1584 | +#define crc_finalize(crc) (crc ^ 0xffffffff) |
1585 | + |
1586 | + |
1587 | +#ifdef __cplusplus |
1588 | +} /* closing brace for extern "C" */ |
1589 | +#endif |
1590 | + |
1591 | +#endif /* __CRC_H__ */ |
1592 | |
1593 | === added file 'mixxx/src/musicbrainz/gzip.cpp' |
1594 | --- mixxx/src/musicbrainz/gzip.cpp 1970-01-01 00:00:00 +0000 |
1595 | +++ mixxx/src/musicbrainz/gzip.cpp 2013-05-22 07:42:27 +0000 |
1596 | @@ -0,0 +1,49 @@ |
1597 | +/***************************************************************************** |
1598 | + * Author: Lukáš Lalinský <info@acoustid.org> * |
1599 | + *****************************************************************************/ |
1600 | + |
1601 | +#include "crc.h" |
1602 | +#include "gzip.h" |
1603 | + |
1604 | +inline QByteArray render32BitInt(unsigned long value) |
1605 | +{ |
1606 | + unsigned char data[4]; |
1607 | + data[0] = (value ) & 255; |
1608 | + data[1] = (value >> 8) & 255; |
1609 | + data[2] = (value >> 16) & 255; |
1610 | + data[3] = (value >> 24) & 255; |
1611 | + return QByteArray((char *)data, 4); |
1612 | +} |
1613 | + |
1614 | +inline unsigned long calculateCrc32(const QByteArray &data) |
1615 | +{ |
1616 | + crc_t crc; |
1617 | + crc = crc_init(); |
1618 | + crc = crc_update(crc, (unsigned char *)data.data(), data.size()); |
1619 | + crc = crc_finalize(crc); |
1620 | + return crc; |
1621 | +} |
1622 | + |
1623 | +QByteArray gzipCompress(const QByteArray &data) |
1624 | +{ |
1625 | + const char header[10] = { |
1626 | + 0x1f, 0x8b, // ID1 + ID2 |
1627 | + 8, // Compression Method |
1628 | + 0, // Flags |
1629 | + 0, 0, 0, 0, // Modification Time |
1630 | + 2, // Extra Flags |
1631 | + 255, // Operating System |
1632 | + }; |
1633 | + |
1634 | + QByteArray compressedData = qCompress(data); |
1635 | + compressedData.remove(0, 6); // Qt size + zlib header |
1636 | + compressedData.remove(compressedData.size() - 4, 4); // zlib footer |
1637 | + |
1638 | + QByteArray result; |
1639 | + result.append(header, 10); |
1640 | + result.append(compressedData); |
1641 | + result.append(render32BitInt(calculateCrc32(data))); |
1642 | + result.append(render32BitInt(data.size())); |
1643 | + return result; |
1644 | +} |
1645 | + |
1646 | |
1647 | === added file 'mixxx/src/musicbrainz/gzip.h' |
1648 | --- mixxx/src/musicbrainz/gzip.h 1970-01-01 00:00:00 +0000 |
1649 | +++ mixxx/src/musicbrainz/gzip.h 2013-05-22 07:42:27 +0000 |
1650 | @@ -0,0 +1,12 @@ |
1651 | +/***************************************************************************** |
1652 | + * Author: Lukáš Lalinský <info@acoustid.org> * |
1653 | + *****************************************************************************/ |
1654 | + |
1655 | +#ifndef FPSUBMIT_GZIP_H_ |
1656 | +#define FPSUBMIT_GZIP_H_ |
1657 | + |
1658 | +#include <QByteArray> |
1659 | + |
1660 | +QByteArray gzipCompress(const QByteArray &data); |
1661 | + |
1662 | +#endif |
1663 | |
1664 | === added file 'mixxx/src/musicbrainz/musicbrainzclient.cpp' |
1665 | --- mixxx/src/musicbrainz/musicbrainzclient.cpp 1970-01-01 00:00:00 +0000 |
1666 | +++ mixxx/src/musicbrainz/musicbrainzclient.cpp 2013-05-22 07:42:27 +0000 |
1667 | @@ -0,0 +1,180 @@ |
1668 | +/***************************************************************************** |
1669 | + * Copyright © 2012 John Maguire <john.maguire@gmail.com> * |
1670 | + * David Sansome <me@davidsansome.com> * |
1671 | + * This work is free. You can redistribute it and/or modify it under the * |
1672 | + * terms of the Do What The Fuck You Want To Public License, Version 2, * |
1673 | + * as published by Sam Hocevar. * |
1674 | + * See http://www.wtfpl.net/ for more details. * |
1675 | + *****************************************************************************/ |
1676 | + |
1677 | +#include <QCoreApplication> |
1678 | +#include <QNetworkReply> |
1679 | +#include <QtNetwork> |
1680 | +#include <QSet> |
1681 | +#include <QXmlStreamReader> |
1682 | + |
1683 | +#include "musicbrainzclient.h" |
1684 | + |
1685 | +const QString MusicBrainzClient::m_TrackUrl = "http://musicbrainz.org/ws/2/recording/"; |
1686 | +const QString MusicBrainzClient::m_DateRegex = "^[12]\\d{3}"; |
1687 | +const int MusicBrainzClient::m_DefaultTimeout = 5000; // msec |
1688 | + |
1689 | +MusicBrainzClient::MusicBrainzClient(QObject* parent) |
1690 | + : QObject(parent), |
1691 | + m_network(this), |
1692 | + m_timeouts(m_DefaultTimeout, this){ |
1693 | +} |
1694 | + |
1695 | +void MusicBrainzClient::start(int id, const QString& mbid) { |
1696 | + typedef QPair<QString, QString> Param; |
1697 | + |
1698 | + QList<Param> parameters; |
1699 | + parameters << Param("inc", "artists+releases+media"); |
1700 | + |
1701 | + QUrl url(m_TrackUrl + mbid); |
1702 | + url.setQueryItems(parameters); |
1703 | + QNetworkRequest req(url); |
1704 | + |
1705 | + QNetworkReply* reply = m_network.get(req); |
1706 | + connect(reply, SIGNAL(finished()), SLOT(requestFinished())); |
1707 | + m_requests[reply] = id; |
1708 | + |
1709 | + m_timeouts.addReply(reply); |
1710 | +} |
1711 | + |
1712 | +void MusicBrainzClient::cancel(int id) { |
1713 | + QNetworkReply* reply = m_requests.key(id); |
1714 | + m_requests.remove(reply); |
1715 | + delete reply; |
1716 | +} |
1717 | + |
1718 | +void MusicBrainzClient::cancelAll() { |
1719 | + qDeleteAll(m_requests.keys()); |
1720 | + m_requests.clear(); |
1721 | +} |
1722 | + |
1723 | +void MusicBrainzClient::requestFinished() { |
1724 | + QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender()); |
1725 | + if (!reply) |
1726 | + return; |
1727 | + |
1728 | + reply->deleteLater(); |
1729 | + if (!m_requests.contains(reply)) |
1730 | + return; |
1731 | + |
1732 | + int id = m_requests.take(reply); |
1733 | + ResultList ret; |
1734 | + |
1735 | + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { |
1736 | + emit finished(id, ret); |
1737 | + return; |
1738 | + } |
1739 | + |
1740 | + QXmlStreamReader reader(reply); |
1741 | + while (!reader.atEnd()) { |
1742 | + if (reader.readNext() == QXmlStreamReader::StartElement |
1743 | + && reader.name() == "recording") { |
1744 | + |
1745 | + ResultList tracks = parseTrack(reader); |
1746 | + foreach (const Result& track, tracks) { |
1747 | + if (!track.m_title.isEmpty()) { |
1748 | + ret << track; |
1749 | + } |
1750 | + } |
1751 | + } |
1752 | + } |
1753 | + emit finished(id, uniqueResults(ret)); |
1754 | +} |
1755 | + |
1756 | +MusicBrainzClient::ResultList MusicBrainzClient::parseTrack(QXmlStreamReader& reader) { |
1757 | + Result result; |
1758 | + QList<Release> releases; |
1759 | + |
1760 | + while (!reader.atEnd()) { |
1761 | + QXmlStreamReader::TokenType type = reader.readNext(); |
1762 | + |
1763 | + if (type == QXmlStreamReader::StartElement) { |
1764 | + QStringRef name = reader.name(); |
1765 | + if (name == "title") { |
1766 | + result.m_title = reader.readElementText(); |
1767 | + } else if (name == "length") { |
1768 | + // convert msec to sec |
1769 | + result.m_duration = reader.readElementText().toInt()*10000000; |
1770 | + } else if (name == "artist") { |
1771 | + parseArtist(reader, result.m_artist); |
1772 | + } else if (name == "release") { |
1773 | + releases << parseRelease(reader); |
1774 | + } |
1775 | + } |
1776 | + |
1777 | + if (type == QXmlStreamReader::EndElement && reader.name() == "recording") { |
1778 | + break; |
1779 | + } |
1780 | + } |
1781 | + |
1782 | + ResultList ret; |
1783 | + foreach (const Release& release, releases) { |
1784 | + ret << release.CopyAndMergeInto(result); |
1785 | + } |
1786 | + return ret; |
1787 | +} |
1788 | + |
1789 | +void MusicBrainzClient::parseArtist(QXmlStreamReader& reader, QString& artist) { |
1790 | + while (!reader.atEnd()) { |
1791 | + QXmlStreamReader::TokenType type = reader.readNext(); |
1792 | + |
1793 | + if (type == QXmlStreamReader::StartElement && reader.name() == "name") { |
1794 | + artist = reader.readElementText(); |
1795 | + } |
1796 | + |
1797 | + if (type == QXmlStreamReader::EndElement && reader.name() == "artist") { |
1798 | + return; |
1799 | + } |
1800 | + } |
1801 | +} |
1802 | + |
1803 | +MusicBrainzClient::Release MusicBrainzClient::parseRelease(QXmlStreamReader& reader) { |
1804 | + Release ret; |
1805 | + |
1806 | + while (!reader.atEnd()) { |
1807 | + QXmlStreamReader::TokenType type = reader.readNext(); |
1808 | + |
1809 | + if (type == QXmlStreamReader::StartElement) { |
1810 | + QStringRef name = reader.name(); |
1811 | + if (name == "title") { |
1812 | + ret.m_album = reader.readElementText(); |
1813 | + } else if (name == "date") { |
1814 | + QRegExp regex(m_DateRegex); |
1815 | + if (regex.indexIn(reader.readElementText()) == 0) { |
1816 | + ret.m_year = regex.cap(0).toInt(); |
1817 | + } |
1818 | + } else if (name == "track-list") { |
1819 | + ret.m_track = reader.attributes().value("offset").toString().toInt() + 1; |
1820 | + consumeCurrentElement(reader); |
1821 | + } |
1822 | + } |
1823 | + |
1824 | + if (type == QXmlStreamReader::EndElement && reader.name() == "release") { |
1825 | + break; |
1826 | + } |
1827 | + } |
1828 | + |
1829 | + return ret; |
1830 | +} |
1831 | + |
1832 | +MusicBrainzClient::ResultList MusicBrainzClient::uniqueResults(const ResultList& results) { |
1833 | + ResultList ret = QSet<Result>::fromList(results).toList(); |
1834 | + qSort(ret); |
1835 | + return ret; |
1836 | +} |
1837 | + |
1838 | +void MusicBrainzClient::consumeCurrentElement(QXmlStreamReader& reader) { |
1839 | + int level = 1; |
1840 | + while (level != 0 && !reader.atEnd()) { |
1841 | + switch (reader.readNext()) { |
1842 | + case QXmlStreamReader::StartElement: ++level; break; |
1843 | + case QXmlStreamReader::EndElement: --level; break; |
1844 | + default: break; |
1845 | + } |
1846 | + } |
1847 | +} |
1848 | |
1849 | === added file 'mixxx/src/musicbrainz/musicbrainzclient.h' |
1850 | --- mixxx/src/musicbrainz/musicbrainzclient.h 1970-01-01 00:00:00 +0000 |
1851 | +++ mixxx/src/musicbrainz/musicbrainzclient.h 2013-05-22 07:42:27 +0000 |
1852 | @@ -0,0 +1,130 @@ |
1853 | +/***************************************************************************** |
1854 | + * Copyright © 2012 John Maguire <john.maguire@gmail.com> * |
1855 | + * David Sansome <me@davidsansome.com> * |
1856 | + * This work is free. You can redistribute it and/or modify it under the * |
1857 | + * terms of the Do What The Fuck You Want To Public License, Version 2, * |
1858 | + * as published by Sam Hocevar. * |
1859 | + * See http://www.wtfpl.net/ for more details. * |
1860 | + *****************************************************************************/ |
1861 | + |
1862 | +#ifndef MUSICBRAINZCLIENT_H |
1863 | +#define MUSICBRAINZCLIENT_H |
1864 | + |
1865 | +#include <QHash> |
1866 | +#include <QMap> |
1867 | +#include <QObject> |
1868 | +#include <QXmlStreamReader> |
1869 | +#include <QtNetwork> |
1870 | + |
1871 | +#include "network.h" |
1872 | + |
1873 | +class MusicBrainzClient : public QObject { |
1874 | + Q_OBJECT |
1875 | + |
1876 | + // Gets metadata for a particular MBID. |
1877 | + // An MBID is created from a fingerprint using chromaprint library. |
1878 | + // You can create one MusicBrainzClient and make multiple requests using it. |
1879 | + // IDs are provided by the caller when a request is started and included in |
1880 | + // the Finished signal - they have no meaning to MusicBrainzClient. |
1881 | + |
1882 | + public: |
1883 | + MusicBrainzClient(QObject* parent = 0); |
1884 | + |
1885 | + struct Result { |
1886 | + Result() : m_duration(0), m_track(0), m_year(-1) {} |
1887 | + |
1888 | + bool operator <(const Result& other) const { |
1889 | + #define cmp(field) \ |
1890 | + if (field < other.field) return true; \ |
1891 | + if (field > other.field) return false; |
1892 | + |
1893 | + cmp(m_track); |
1894 | + cmp(m_year); |
1895 | + cmp(m_title); |
1896 | + cmp(m_artist); |
1897 | + return false; |
1898 | + |
1899 | + #undef cmp |
1900 | + } |
1901 | + |
1902 | + bool operator ==(const Result& other) const { |
1903 | + return m_title == other.m_title && |
1904 | + m_artist == other.m_artist && |
1905 | + m_album == other.m_album && |
1906 | + m_duration == other.m_duration && |
1907 | + m_track == other.m_track && |
1908 | + m_year == other.m_year; |
1909 | + } |
1910 | + |
1911 | + QString m_title; |
1912 | + QString m_artist; |
1913 | + QString m_album; |
1914 | + int m_duration; |
1915 | + int m_track; |
1916 | + int m_year; |
1917 | + }; |
1918 | + typedef QList<Result> ResultList; |
1919 | + |
1920 | + |
1921 | + // Starts a request and returns immediately. finished() will be emitted |
1922 | + // later with the same ID. |
1923 | + void start(int id, const QString& mbid); |
1924 | + static void consumeCurrentElement(QXmlStreamReader& reader); |
1925 | + |
1926 | + // Cancels the request with the given ID. Finished() will never be emitted |
1927 | + // for that ID. Does nothing if there is no request with the given ID. |
1928 | + void cancel(int id); |
1929 | + |
1930 | + // Cancels all requests. Finished() will never be emitted for any pending |
1931 | + // requests. |
1932 | + void cancelAll(); |
1933 | + |
1934 | + signals: |
1935 | + // Finished signal emitted when fechting songs tags |
1936 | + void finished(int id, const MusicBrainzClient::ResultList& result); |
1937 | + |
1938 | + private slots: |
1939 | + void requestFinished(); |
1940 | + |
1941 | + private: |
1942 | + struct Release { |
1943 | + Release() : m_track(0), m_year(0) {} |
1944 | + |
1945 | + Result CopyAndMergeInto(const Result& orig) const { |
1946 | + Result ret(orig); |
1947 | + ret.m_album = m_album; |
1948 | + ret.m_track = m_track; |
1949 | + ret.m_year = m_year; |
1950 | + return ret; |
1951 | + } |
1952 | + |
1953 | + QString m_album; |
1954 | + int m_track; |
1955 | + int m_year; |
1956 | + }; |
1957 | + |
1958 | + static ResultList parseTrack(QXmlStreamReader& reader); |
1959 | + static void parseArtist(QXmlStreamReader& reader, QString& artist); |
1960 | + static Release parseRelease(QXmlStreamReader& reader); |
1961 | + static ResultList uniqueResults(const ResultList& results); |
1962 | + |
1963 | + private: |
1964 | + static const QString m_TrackUrl; |
1965 | + static const QString m_DateRegex; |
1966 | + static const int m_DefaultTimeout; |
1967 | + |
1968 | + QNetworkAccessManager m_network; |
1969 | + NetworkTimeouts m_timeouts; |
1970 | + QMap<QNetworkReply*, int> m_requests; |
1971 | +}; |
1972 | + |
1973 | +inline uint qHash(const MusicBrainzClient::Result& result) { |
1974 | + return qHash(result.m_album) ^ |
1975 | + qHash(result.m_artist) ^ |
1976 | + result.m_duration ^ |
1977 | + qHash(result.m_title) ^ |
1978 | + result.m_track ^ |
1979 | + result.m_year; |
1980 | +} |
1981 | + |
1982 | +#endif // MUSICBRAINZCLIENT_H |
1983 | |
1984 | === added file 'mixxx/src/musicbrainz/tagfetcher.cpp' |
1985 | --- mixxx/src/musicbrainz/tagfetcher.cpp 1970-01-01 00:00:00 +0000 |
1986 | +++ mixxx/src/musicbrainz/tagfetcher.cpp 2013-05-22 07:42:27 +0000 |
1987 | @@ -0,0 +1,121 @@ |
1988 | +/***************************************************************************** |
1989 | + * Copyright © 2012 John Maguire <john.maguire@gmail.com> * |
1990 | + * David Sansome <me@davidsansome.com> * |
1991 | + * This work is free. You can redistribute it and/or modify it under the * |
1992 | + * terms of the Do What The Fuck You Want To Public License, Version 2, * |
1993 | + * as published by Sam Hocevar. * |
1994 | + * See http://www.wtfpl.net/ for more details. * |
1995 | + *****************************************************************************/ |
1996 | + |
1997 | +#include <QFuture> |
1998 | +#include <QUrl> |
1999 | +#include <QtConcurrentMap> |
2000 | + |
2001 | +#include "musicbrainz/tagfetcher.h" |
2002 | +#include "musicbrainz/chromaprinter.h" |
2003 | +#include "musicbrainz/musicbrainzclient.h" |
2004 | + |
2005 | +TagFetcher::TagFetcher(QObject* parent) |
2006 | + : QObject(parent), |
2007 | + m_pFingerprintWatcher(NULL), |
2008 | + m_AcoustidClient(this), |
2009 | + m_MusicbrainzClient(this) { |
2010 | + connect(&m_AcoustidClient, SIGNAL(finished(int,QString)), |
2011 | + this, SLOT(mbidFound(int,QString))); |
2012 | + connect(&m_MusicbrainzClient, SIGNAL(finished(int,MusicBrainzClient::ResultList)), |
2013 | + this, SLOT(tagsFetched(int,MusicBrainzClient::ResultList))); |
2014 | +} |
2015 | + |
2016 | +QString TagFetcher::getFingerprint(const TrackPointer tio) { |
2017 | + return chromaprinter(NULL).getFingerPrint(tio); |
2018 | +} |
2019 | + |
2020 | +void TagFetcher::startFetch(const TrackPointer track) { |
2021 | + cancel(); |
2022 | + // qDebug() << "start to fetch track metadata"; |
2023 | + QList<TrackPointer> tracks; |
2024 | + tracks.append(track); |
2025 | + m_tracks = tracks; |
2026 | + |
2027 | + QFuture<QString> future = QtConcurrent::mapped(m_tracks, getFingerprint); |
2028 | + m_pFingerprintWatcher = new QFutureWatcher<QString>(this); |
2029 | + m_pFingerprintWatcher->setFuture(future); |
2030 | + connect(m_pFingerprintWatcher, SIGNAL(resultReadyAt(int)), |
2031 | + SLOT(fingerprintFound(int))); |
2032 | + |
2033 | + foreach (const TrackPointer ptrack, m_tracks) { |
2034 | + emit fetchProgress(tr("Fingerprinting track")); |
2035 | + } |
2036 | +} |
2037 | + |
2038 | +void TagFetcher::cancel() { |
2039 | + // qDebug()<< "Cancel tagfetching"; |
2040 | + if (m_pFingerprintWatcher) { |
2041 | + m_pFingerprintWatcher->cancel(); |
2042 | + |
2043 | + delete m_pFingerprintWatcher; |
2044 | + m_pFingerprintWatcher = NULL; |
2045 | + } |
2046 | + |
2047 | + m_AcoustidClient.cancelAll(); |
2048 | + m_MusicbrainzClient.cancelAll(); |
2049 | + m_tracks.clear(); |
2050 | +} |
2051 | + |
2052 | +void TagFetcher::fingerprintFound(int index) { |
2053 | + QFutureWatcher<QString>* watcher = reinterpret_cast<QFutureWatcher<QString>*>(sender()); |
2054 | + if (!watcher || index >= m_tracks.count()) { |
2055 | + return; |
2056 | + } |
2057 | + |
2058 | + const QString fingerprint = watcher->resultAt(index); |
2059 | + const TrackPointer ptrack = m_tracks[index]; |
2060 | + |
2061 | + if (fingerprint.isEmpty()) { |
2062 | + emit resultAvailable(ptrack, QList<TrackPointer>()); |
2063 | + return; |
2064 | + } |
2065 | + |
2066 | + emit fetchProgress(tr("Identifying track")); |
2067 | + // qDebug() << "start to look up the MBID"; |
2068 | + m_AcoustidClient.start(index, fingerprint, ptrack->getDuration()); |
2069 | +} |
2070 | + |
2071 | +void TagFetcher::mbidFound(int index, const QString& mbid) { |
2072 | + if (index >= m_tracks.count()) { |
2073 | + return; |
2074 | + } |
2075 | + |
2076 | + const TrackPointer pTrack = m_tracks[index]; |
2077 | + |
2078 | + if (mbid.isEmpty()) { |
2079 | + emit resultAvailable(pTrack, QList<TrackPointer>()); |
2080 | + return; |
2081 | + } |
2082 | + |
2083 | + emit fetchProgress(tr("Downloading Metadata")); |
2084 | + //qDebug() << "start to fetch tags from MB"; |
2085 | + m_MusicbrainzClient.start(index, mbid); |
2086 | +} |
2087 | + |
2088 | +void TagFetcher::tagsFetched(int index, const MusicBrainzClient::ResultList& results) { |
2089 | + if (index >= m_tracks.count()) { |
2090 | + return; |
2091 | + } |
2092 | + // qDebug() << "Tagfetcher got musicbrainz results and now parses them"; |
2093 | + const TrackPointer originalTrack = m_tracks[index]; |
2094 | + QList<TrackPointer> tracksGuessed; |
2095 | + |
2096 | + foreach (const MusicBrainzClient::Result& result, results) { |
2097 | + TrackPointer track(new TrackInfoObject(originalTrack->getLocation(),false), |
2098 | + &QObject::deleteLater); |
2099 | + track->setTitle(result.m_title); |
2100 | + track->setArtist(result.m_artist); |
2101 | + track->setAlbum(result.m_album); |
2102 | + track->setDuration(result.m_duration); |
2103 | + track->setTrackNumber(QString::number(result.m_track)); |
2104 | + track->setYear(QString::number(result.m_year)); |
2105 | + tracksGuessed << track; |
2106 | + } |
2107 | + emit resultAvailable(originalTrack, tracksGuessed); |
2108 | +} |
2109 | |
2110 | === added file 'mixxx/src/musicbrainz/tagfetcher.h' |
2111 | --- mixxx/src/musicbrainz/tagfetcher.h 1970-01-01 00:00:00 +0000 |
2112 | +++ mixxx/src/musicbrainz/tagfetcher.h 2013-05-22 07:42:27 +0000 |
2113 | @@ -0,0 +1,58 @@ |
2114 | +/***************************************************************************** |
2115 | + * Copyright © 2012 John Maguire <john.maguire@gmail.com> * |
2116 | + * David Sansome <me@davidsansome.com> * |
2117 | + * This work is free. You can redistribute it and/or modify it under the * |
2118 | + * terms of the Do What The Fuck You Want To Public License, Version 2, * |
2119 | + * as published by Sam Hocevar. * |
2120 | + * See http://www.wtfpl.net/ for more details. * |
2121 | + *****************************************************************************/ |
2122 | + |
2123 | +#ifndef TAGFETCHER_H |
2124 | +#define TAGFETCHER_H |
2125 | + |
2126 | +#include <QFutureWatcher> |
2127 | +#include <QObject> |
2128 | + |
2129 | +#include "musicbrainz/musicbrainzclient.h" |
2130 | +#include "musicbrainz/acoustidclient.h" |
2131 | +#include "trackinfoobject.h" |
2132 | + |
2133 | + |
2134 | +class TagFetcher : public QObject { |
2135 | + Q_OBJECT |
2136 | + |
2137 | + // High level interface to Fingerprinter, AcoustidClient and |
2138 | + // MusicBrainzClient. |
2139 | + |
2140 | + public: |
2141 | + TagFetcher(QObject* parent = 0); |
2142 | + |
2143 | + void startFetch(const TrackPointer track); |
2144 | + |
2145 | + public slots: |
2146 | + void cancel(); |
2147 | + |
2148 | + signals: |
2149 | + void resultAvailable(const TrackPointer originalTrack, |
2150 | + const QList<TrackPointer>& tracksGuessed); |
2151 | + void fetchProgress(QString); |
2152 | + |
2153 | + private slots: |
2154 | + void fingerprintFound(int index); |
2155 | + void mbidFound(int index, const QString& mbid); |
2156 | + void tagsFetched(int index, const MusicBrainzClient::ResultList& result); |
2157 | + |
2158 | + private: |
2159 | + // has to be static so we can call it with QtConcurrent and have a nice |
2160 | + // responsive UI while the fingerprint is calculated |
2161 | + static QString getFingerprint(const TrackPointer tio); |
2162 | + |
2163 | + QFutureWatcher<QString>* m_pFingerprintWatcher; |
2164 | + AcoustidClient m_AcoustidClient; |
2165 | + MusicBrainzClient m_MusicbrainzClient; |
2166 | + |
2167 | + // Code can already be run on an arbitrary number of input tracks |
2168 | + QList<TrackPointer> m_tracks; |
2169 | +}; |
2170 | + |
2171 | +#endif // TAGFETCHER_H |
2172 | |
2173 | === added file 'mixxx/src/network.cpp' |
2174 | --- mixxx/src/network.cpp 1970-01-01 00:00:00 +0000 |
2175 | +++ mixxx/src/network.cpp 2013-05-22 07:42:27 +0000 |
2176 | @@ -0,0 +1,73 @@ |
2177 | +/***************************************************************************** |
2178 | + * Copyright © 2012 John Maguire <john.maguire@gmail.com> * |
2179 | + * David Sansome <me@davidsansome.com> * |
2180 | + * This work is free. You can redistribute it and/or modify it under the * |
2181 | + * terms of the Do What The Fuck You Want To Public License, Version 2, * |
2182 | + * as published by Sam Hocevar. * |
2183 | + * See http://www.wtfpl.net/ for more details. * |
2184 | + *****************************************************************************/ |
2185 | + |
2186 | +#include <QCoreApplication> |
2187 | +#include <QDir> |
2188 | +#include <QNetworkAccessManager> |
2189 | +#include <QNetworkDiskCache> |
2190 | +#include <QNetworkReply> |
2191 | + |
2192 | +#include "network.h" |
2193 | + |
2194 | +NetworkAccessManager::NetworkAccessManager(QObject* parent) |
2195 | + : QNetworkAccessManager(parent) { |
2196 | +} |
2197 | + |
2198 | +QNetworkReply* NetworkAccessManager::createRequest(Operation op, |
2199 | + const QNetworkRequest& request, |
2200 | + QIODevice* outgoingData) { |
2201 | + QNetworkRequest new_request(request); |
2202 | + new_request.setRawHeader("User-Agent", QString("%1 %2").arg( |
2203 | + QCoreApplication::applicationName(), |
2204 | + QCoreApplication::applicationVersion()).toUtf8()); |
2205 | + |
2206 | + if (op == QNetworkAccessManager::PostOperation && |
2207 | + !new_request.header(QNetworkRequest::ContentTypeHeader).isValid()) { |
2208 | + new_request.setHeader(QNetworkRequest::ContentTypeHeader, |
2209 | + "application/x-www-form-urlencoded"); |
2210 | + } |
2211 | + |
2212 | + // Prefer the cache unless the caller has changed the setting already |
2213 | + if (request.attribute(QNetworkRequest::CacheLoadControlAttribute).toInt() |
2214 | + == QNetworkRequest::PreferNetwork) { |
2215 | + new_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, |
2216 | + QNetworkRequest::PreferCache); |
2217 | + } |
2218 | + |
2219 | + return QNetworkAccessManager::createRequest(op, new_request, outgoingData); |
2220 | +} |
2221 | + |
2222 | + |
2223 | +NetworkTimeouts::NetworkTimeouts(int timeout_msec, QObject* parent) |
2224 | + : QObject(parent), |
2225 | + m_timeout_msec(timeout_msec) { |
2226 | +} |
2227 | + |
2228 | +void NetworkTimeouts::addReply(QNetworkReply* reply) { |
2229 | + if (m_timers.contains(reply)) |
2230 | + return; |
2231 | + |
2232 | + connect(reply, SIGNAL(destroyed()), SLOT(replyFinished())); |
2233 | + connect(reply, SIGNAL(finished()), SLOT(replyFinished())); |
2234 | + m_timers[reply] = startTimer(m_timeout_msec); |
2235 | +} |
2236 | + |
2237 | +void NetworkTimeouts::replyFinished() { |
2238 | + QNetworkReply* reply = reinterpret_cast<QNetworkReply*>(sender()); |
2239 | + if (m_timers.contains(reply)) { |
2240 | + killTimer(m_timers.take(reply)); |
2241 | + } |
2242 | +} |
2243 | + |
2244 | +void NetworkTimeouts::timerEvent(QTimerEvent* e) { |
2245 | + QNetworkReply* reply = m_timers.key(e->timerId()); |
2246 | + if (reply) { |
2247 | + reply->abort(); |
2248 | + } |
2249 | +} |
2250 | |
2251 | === added file 'mixxx/src/network.h' |
2252 | --- mixxx/src/network.h 1970-01-01 00:00:00 +0000 |
2253 | +++ mixxx/src/network.h 2013-05-22 07:42:27 +0000 |
2254 | @@ -0,0 +1,49 @@ |
2255 | +/***************************************************************************** |
2256 | + * Copyright © 2012 John Maguire <john.maguire@gmail.com> * |
2257 | + * David Sansome <me@davidsansome.com> * |
2258 | + * This work is free. You can redistribute it and/or modify it under the * |
2259 | + * terms of the Do What The Fuck You Want To Public License, Version 2, * |
2260 | + * as published by Sam Hocevar. * |
2261 | + * See http://www.wtfpl.net/ for more details. * |
2262 | + *****************************************************************************/ |
2263 | + |
2264 | +#ifndef NETWORK_H |
2265 | +#define NETWORK_H |
2266 | + |
2267 | +#include <QAbstractNetworkCache> |
2268 | +#include <QNetworkAccessManager> |
2269 | +#include <QNetworkReply> |
2270 | + |
2271 | +class NetworkAccessManager : public QNetworkAccessManager { |
2272 | + Q_OBJECT |
2273 | + |
2274 | + public: |
2275 | + NetworkAccessManager(QObject* parent = 0); |
2276 | + |
2277 | + protected: |
2278 | + QNetworkReply* createRequest(Operation op, const QNetworkRequest& request, |
2279 | + QIODevice* outgoingData); |
2280 | +}; |
2281 | + |
2282 | + |
2283 | +class NetworkTimeouts : public QObject { |
2284 | + Q_OBJECT |
2285 | + |
2286 | + public: |
2287 | + NetworkTimeouts(int timeout_msec, QObject* parent = 0); |
2288 | + |
2289 | + void addReply(QNetworkReply* reply); |
2290 | + void setTimeout(int msec) { m_timeout_msec = msec; } |
2291 | + |
2292 | + protected: |
2293 | + void timerEvent(QTimerEvent* e); |
2294 | + |
2295 | + private slots: |
2296 | + void replyFinished(); |
2297 | + |
2298 | + private: |
2299 | + int m_timeout_msec; |
2300 | + QMap<QNetworkReply*, int> m_timers; |
2301 | +}; |
2302 | + |
2303 | +#endif // NETWORK_H |
2304 | |
2305 | === modified file 'mixxx/src/widget/wtracktableview.cpp' |
2306 | --- mixxx/src/widget/wtracktableview.cpp 2013-05-21 03:02:42 +0000 |
2307 | +++ mixxx/src/widget/wtracktableview.cpp 2013-05-22 07:42:27 +0000 |
2308 | @@ -25,14 +25,20 @@ |
2309 | WTRACKTABLEVIEW_VSCROLLBARPOS_KEY)), |
2310 | m_pConfig(pConfig), |
2311 | m_pTrackCollection(pTrackCollection), |
2312 | + m_DlgTagFetcher(NULL) , |
2313 | m_sorting(sorting) { |
2314 | // Give a NULL parent because otherwise it inherits our style which can make |
2315 | // it unreadable. Bug #673411 |
2316 | - m_pTrackInfo = new DlgTrackInfo(NULL); |
2317 | + m_pTrackInfo = new DlgTrackInfo(NULL,m_DlgTagFetcher); |
2318 | connect(m_pTrackInfo, SIGNAL(next()), |
2319 | this, SLOT(slotNextTrackInfo())); |
2320 | connect(m_pTrackInfo, SIGNAL(previous()), |
2321 | this, SLOT(slotPrevTrackInfo())); |
2322 | + connect(&m_DlgTagFetcher, SIGNAL(next()), |
2323 | + this, SLOT(slotNextDlgTagFetcher())); |
2324 | + connect(&m_DlgTagFetcher, SIGNAL(previous()), |
2325 | + this, SLOT(slotPrevDlgTagFetcher())); |
2326 | + |
2327 | |
2328 | connect(&m_loadTrackMapper, SIGNAL(mapped(QString)), |
2329 | this, SLOT(loadSelectionToGroup(QString))); |
2330 | @@ -75,8 +81,8 @@ |
2331 | this, SLOT(addSelectionToCrate(int))); |
2332 | } |
2333 | |
2334 | -WTrackTableView::~WTrackTableView() |
2335 | -{ |
2336 | +WTrackTableView::~WTrackTableView() { |
2337 | + qDebug() << "~WTrackTableView()"; |
2338 | WTrackTableViewHeader* pHeader = |
2339 | dynamic_cast<WTrackTableViewHeader*>(horizontalHeader()); |
2340 | if (pHeader) { |
2341 | @@ -84,6 +90,7 @@ |
2342 | } |
2343 | |
2344 | delete m_pReloadMetadataAct; |
2345 | + delete m_pReloadMetadataFromMusicBrainzAct; |
2346 | delete m_pAddToPreviewDeck; |
2347 | delete m_pAutoDJAct; |
2348 | delete m_pAutoDJTopAct; |
2349 | @@ -290,6 +297,10 @@ |
2350 | connect(m_pReloadMetadataAct, SIGNAL(triggered()), |
2351 | this, SLOT(slotReloadTrackMetadata())); |
2352 | |
2353 | + m_pReloadMetadataFromMusicBrainzAct = new QAction(tr("Reload from Musicbrainz"),this); |
2354 | + connect(m_pReloadMetadataFromMusicBrainzAct, SIGNAL(triggered()), |
2355 | + this, SLOT(slotShowDlgTagFetcher())); |
2356 | + |
2357 | m_pAddToPreviewDeck = new QAction(tr("Load to Preview Deck"), this); |
2358 | // currently there is only one preview deck so just map it here. |
2359 | QString previewDeckGroup = PlayerManager::groupForPreviewDeck(0); |
2360 | @@ -456,6 +467,42 @@ |
2361 | m_pTrackInfo->show(); |
2362 | } |
2363 | |
2364 | +void WTrackTableView::slotNextDlgTagFetcher() { |
2365 | + QModelIndex nextRow = currentTrackInfoIndex.sibling( |
2366 | + currentTrackInfoIndex.row()+1, currentTrackInfoIndex.column()); |
2367 | + if (nextRow.isValid()) |
2368 | + showDlgTagFetcher(nextRow); |
2369 | +} |
2370 | + |
2371 | +void WTrackTableView::slotPrevDlgTagFetcher() { |
2372 | + QModelIndex prevRow = currentTrackInfoIndex.sibling( |
2373 | + currentTrackInfoIndex.row()-1, currentTrackInfoIndex.column()); |
2374 | + if (prevRow.isValid()) |
2375 | + showDlgTagFetcher(prevRow); |
2376 | +} |
2377 | + |
2378 | +void WTrackTableView::showDlgTagFetcher(QModelIndex index) { |
2379 | + TrackModel* trackModel = getTrackModel(); |
2380 | + |
2381 | + if (!trackModel) { |
2382 | + return; |
2383 | + } |
2384 | + |
2385 | + TrackPointer pTrack = trackModel->getTrack(index); |
2386 | + // NULL is fine |
2387 | + m_DlgTagFetcher.init(pTrack); |
2388 | + currentTrackInfoIndex = index; |
2389 | + m_DlgTagFetcher.show(); |
2390 | +} |
2391 | + |
2392 | +void WTrackTableView::slotShowDlgTagFetcher(){ |
2393 | + QModelIndexList indices = selectionModel()->selectedRows(); |
2394 | + |
2395 | + if (indices.size() > 0) { |
2396 | + showDlgTagFetcher(indices[0]); |
2397 | + } |
2398 | +} |
2399 | + |
2400 | void WTrackTableView::contextMenuEvent(QContextMenuEvent* event) { |
2401 | QModelIndexList indices = selectionModel()->selectedRows(); |
2402 | |
2403 | @@ -613,8 +660,10 @@ |
2404 | m_pMenu->addSeparator(); |
2405 | if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_RELOADMETADATA)) { |
2406 | m_pMenu->addAction(m_pReloadMetadataAct); |
2407 | + m_pReloadMetadataFromMusicBrainzAct->setEnabled(oneSongSelected); |
2408 | + m_pMenu->addAction(m_pReloadMetadataFromMusicBrainzAct); |
2409 | } |
2410 | - // REMOVE and HIDE should not be at the first menu position to avoid excitedly clicks |
2411 | + // REMOVE and HIDE should not be at the first menu position to avoid accidental clicks |
2412 | if (modelHasCapabilities(TrackModel::TRACKMODELCAPS_REMOVE)) { |
2413 | m_pRemoveAct->setEnabled(!locked); |
2414 | m_pMenu->addAction(m_pRemoveAct); |
2415 | |
2416 | === modified file 'mixxx/src/widget/wtracktableview.h' |
2417 | --- mixxx/src/widget/wtracktableview.h 2013-05-21 03:02:42 +0000 |
2418 | +++ mixxx/src/widget/wtracktableview.h 2013-05-22 07:42:27 +0000 |
2419 | @@ -9,6 +9,7 @@ |
2420 | #include "library/libraryview.h" |
2421 | #include "library/trackmodel.h" // Can't forward declare enums |
2422 | #include "widget/wlibrarytableview.h" |
2423 | +#include "dlgtagfetcher.h" |
2424 | |
2425 | class ControlObjectThreadMain; |
2426 | class DlgTrackInfo; |
2427 | @@ -44,8 +45,11 @@ |
2428 | void slotHide(); |
2429 | void slotOpenInFileBrowser(); |
2430 | void slotShowTrackInfo(); |
2431 | + void slotShowDlgTagFetcher(); |
2432 | void slotNextTrackInfo(); |
2433 | + void slotNextDlgTagFetcher(); |
2434 | void slotPrevTrackInfo(); |
2435 | + void slotPrevDlgTagFetcher(); |
2436 | void slotSendToAutoDJ(); |
2437 | void slotSendToAutoDJTop(); |
2438 | void slotReloadTrackMetadata(); |
2439 | @@ -61,6 +65,7 @@ |
2440 | private: |
2441 | void sendToAutoDJ(bool bTop); |
2442 | void showTrackInfo(QModelIndex index); |
2443 | + void showDlgTagFetcher(QModelIndex index); |
2444 | void createActions(); |
2445 | void dragMoveEvent(QDragMoveEvent * event); |
2446 | void dragEnterEvent(QDragEnterEvent * event); |
2447 | @@ -81,6 +86,7 @@ |
2448 | QSignalMapper m_loadTrackMapper; |
2449 | |
2450 | DlgTrackInfo* m_pTrackInfo; |
2451 | + DlgTagFetcher m_DlgTagFetcher; |
2452 | QModelIndex currentTrackInfoIndex; |
2453 | |
2454 | |
2455 | @@ -94,6 +100,7 @@ |
2456 | |
2457 | // Reload Track Metadata Action: |
2458 | QAction *m_pReloadMetadataAct; |
2459 | + QAction *m_pReloadMetadataFromMusicBrainzAct; |
2460 | |
2461 | // Load Track to PreviewDeck |
2462 | QAction* m_pAddToPreviewDeck; |
Looks good to me on my first skim. I need to take a look src/chromaprint more closely but for now I'm assuming it works as expected.
* void WTrackTableView ::showDlgTagFet cher(QModelInde x index)
What about when multiple tracks are selected?