summaryrefslogtreecommitdiffstats
path: root/src/translators/freedbimporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/translators/freedbimporter.cpp')
-rw-r--r--src/translators/freedbimporter.cpp556
1 files changed, 556 insertions, 0 deletions
diff --git a/src/translators/freedbimporter.cpp b/src/translators/freedbimporter.cpp
new file mode 100644
index 0000000..14d92d8
--- /dev/null
+++ b/src/translators/freedbimporter.cpp
@@ -0,0 +1,556 @@
+/***************************************************************************
+ copyright : (C) 2004-2006 by Robby Stephenson
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of version 2 of the GNU General Public License as *
+ * published by the Free Software Foundation; *
+ * *
+ ***************************************************************************/
+
+#include "freedbimporter.h"
+#include "../collections/musiccollection.h"
+#include "../entry.h"
+#include "../field.h"
+#include "../latin1literal.h"
+#include "../tellico_utils.h"
+#include "../tellico_debug.h"
+#include "../tellico_kernel.h"
+#include "../progressmanager.h"
+
+#include <config.h>
+
+#ifdef HAVE_KCDDB
+#ifdef QT_NO_CAST_ASCII
+#define HAD_QT_NO_CAST_ASCII
+#undef QT_NO_CAST_ASCII
+#endif
+#include <libkcddb/client.h>
+#ifdef HAD_QT_NO_CAST_ASCII
+#define QT_NO_CAST_ASCII
+#undef HAD_QT_NO_CAST_ASCII
+#endif
+#endif
+
+#include <kcombobox.h>
+#include <kconfig.h>
+#include <kapplication.h>
+#include <kinputdialog.h>
+
+#include <qfile.h>
+#include <qdir.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qgroupbox.h>
+#include <qwhatsthis.h>
+#include <qradiobutton.h>
+#include <qbuttongroup.h>
+#include <qhbox.h>
+#include <qcheckbox.h>
+
+using Tellico::Import::FreeDBImporter;
+
+FreeDBImporter::FreeDBImporter() : Tellico::Import::Importer(), m_coll(0), m_widget(0), m_cancelled(false) {
+}
+
+bool FreeDBImporter::canImport(int type) const {
+ return type == Data::Collection::Album;
+}
+
+Tellico::Data::CollPtr FreeDBImporter::collection() {
+ if(m_coll) {
+ return m_coll;
+ }
+
+ m_cancelled = false;
+ if(m_radioCDROM->isChecked()) {
+ readCDROM();
+ } else {
+ readCache();
+ }
+ if(m_cancelled) {
+ m_coll = 0;
+ }
+ return m_coll;
+}
+
+void FreeDBImporter::readCDROM() {
+#ifdef HAVE_KCDDB
+ QString drivePath = m_driveCombo->currentText();
+ if(drivePath.isEmpty()) {
+ setStatusMessage(i18n("<qt>Tellico was unable to access the CD-ROM device - <i>%1</i>.</qt>").arg(drivePath));
+ myDebug() << "FreeDBImporter::readCDROM() - no drive!" << endl;
+ return;
+ }
+
+ // now it's ok to add device to saved list
+ m_driveCombo->insertItem(drivePath);
+ QStringList drives;
+ for(int i = 0; i < m_driveCombo->count(); ++i) {
+ if(drives.findIndex(m_driveCombo->text(i)) == -1) {
+ drives += m_driveCombo->text(i);
+ }
+ }
+
+ {
+ KConfigGroup config(KGlobal::config(), QString::fromLatin1("ImportOptions - FreeDB"));
+ config.writeEntry("CD-ROM Devices", drives);
+ config.writeEntry("Last Device", drivePath);
+ config.writeEntry("Cache Files Only", false);
+ }
+
+ QCString drive = QFile::encodeName(drivePath);
+ QValueList<uint> lengths;
+ KCDDB::TrackOffsetList list;
+#if 0
+ // a1107d0a - Kruder & Dorfmeister - The K&D Sessions - Disc One.
+/* list
+ << 150 // First track start.
+ << 29462
+ << 66983
+ << 96785
+ << 135628
+ << 168676
+ << 194147
+ << 222158
+ << 247076
+ << 278203 // Last track start.
+ << 10 // Disc start.
+ << 316732; // Disc end.
+*/
+ list
+ << 150 // First track start.
+ << 3296
+ << 14437
+ << 41279
+ << 51362
+ << 56253
+ << 59755
+ << 61324
+ << 66059
+ << 69073
+ << 77790
+ << 83214
+ << 89726
+ << 92078
+ << 106325
+ << 113117
+ << 116040
+ << 119877
+ << 124377
+ << 145466
+ << 157583
+ << 167208
+ << 173486
+ << 180120
+ << 185279
+ << 193270
+ << 206451
+ << 217303 // Last track start.
+ << 10 // Disc start.
+ << 224925; // Disc end.
+/*
+ list
+ << 150
+ << 106965
+ << 127220
+ << 151925
+ << 176085
+ << 5
+ << 234500;
+*/
+#else
+ list = offsetList(drive, lengths);
+#endif
+
+ if(list.isEmpty()) {
+ setStatusMessage(i18n("<qt>Tellico was unable to access the CD-ROM device - <i>%1</i>.</qt>").arg(drivePath));
+ return;
+ }
+// myDebug() << KCDDB::CDDB::trackOffsetListToId(list) << endl;
+// for(KCDDB::TrackOffsetList::iterator it = list.begin(); it != list.end(); ++it) {
+// myDebug() << *it << endl;
+// }
+
+ // the result info, could be multiple ones
+ KCDDB::CDInfo info;
+ KCDDB::Client client;
+ client.setBlockingMode(true);
+ KCDDB::CDDB::Result r = client.lookup(list);
+ // KCDDB doesn't return MultipleRecordFound properly, so check outselves
+ if(r == KCDDB::CDDB::MultipleRecordFound || client.lookupResponse().count() > 1) {
+ QStringList list;
+ KCDDB::CDInfoList infoList = client.lookupResponse();
+ for(KCDDB::CDInfoList::iterator it = infoList.begin(); it != infoList.end(); ++it) {
+ list.append(QString::fromLatin1("%1, %2, %3").arg((*it).artist)
+ .arg((*it).title)
+ .arg((*it).genre));
+ }
+
+ // switch back to pointer cursor
+ GUI::CursorSaver cs(Qt::arrowCursor);
+ bool ok;
+ QString res = KInputDialog::getItem(i18n("Select CDDB Entry"),
+ i18n("Select a CDDB entry:"),
+ list, 0, false, &ok,
+ Kernel::self()->widget());
+ if(ok) {
+ uint i = 0;
+ for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it, ++i) {
+ if(*it == res) {
+ break;
+ }
+ }
+ if(i < infoList.size()) {
+ info = infoList[i];
+ }
+ } else { // cancelled dialog
+ m_cancelled = true;
+ }
+ } else if(r == KCDDB::CDDB::Success) {
+ info = client.bestLookupResponse();
+ } else {
+// myDebug() << "FreeDBImporter::readCDROM() - no success! Return value = " << r << endl;
+ QString s;
+ switch(r) {
+ case KCDDB::CDDB::NoRecordFound:
+ s = i18n("<qt>No records were found to match the CD.</qt>");
+ break;
+ case KCDDB::CDDB::ServerError:
+ myDebug() << "Server Error" << endl;
+ break;
+ case KCDDB::CDDB::HostNotFound:
+ myDebug() << "Host Not Found" << endl;
+ break;
+ case KCDDB::CDDB::NoResponse:
+ myDebug() << "No Response" << endl;
+ break;
+ case KCDDB::CDDB::UnknownError:
+ myDebug() << "Unknown Error" << endl;
+ break;
+ default:
+ break;
+ }
+ if(s.isEmpty()) {
+ s = i18n("<qt>Tellico was unable to complete the CD lookup.</qt>");
+ }
+ setStatusMessage(s);
+ return;
+ }
+
+ if(!info.isValid()) {
+ // go ahead and try to read cd-text if we weren't cancelled
+ // could be the case we don't have net access
+ if(!m_cancelled) {
+ readCDText(drive);
+ }
+ return;
+ }
+
+ m_coll = new Data::MusicCollection(true);
+
+ Data::EntryPtr entry = new Data::Entry(m_coll);
+ // obviously a CD
+ entry->setField(QString::fromLatin1("medium"), i18n("Compact Disc"));
+ entry->setField(QString::fromLatin1("title"), info.title);
+ entry->setField(QString::fromLatin1("artist"), info.artist);
+ entry->setField(QString::fromLatin1("genre"), info.genre);
+ if(info.year > 0) {
+ entry->setField(QString::fromLatin1("year"), QString::number(info.year));
+ }
+ entry->setField(QString::fromLatin1("keyword"), info.category);
+ QString extd = info.extd;
+ extd.replace('\n', QString::fromLatin1("<br/>"));
+ entry->setField(QString::fromLatin1("comments"), extd);
+
+ QStringList trackList;
+ KCDDB::TrackInfoList t = info.trackInfoList;
+ for(uint i = 0; i < t.count(); ++i) {
+#if KDE_IS_VERSION(3,4,90)
+ QString s = t[i].get(QString::fromLatin1("title")).toString() + "::" + info.artist;
+#else
+ QString s = t[i].title + "::" + info.artist;
+#endif
+ if(i < lengths.count()) {
+ s += "::" + Tellico::minutes(lengths[i]);
+ }
+ trackList << s;
+ // TODO: KDE4 will probably have track length too
+ }
+ entry->setField(QString::fromLatin1("track"), trackList.join(QString::fromLatin1("; ")));
+
+ m_coll->addEntries(entry);
+ readCDText(drive);
+#endif
+}
+
+void FreeDBImporter::readCache() {
+#ifdef HAVE_KCDDB
+ {
+ // remember the import options
+ KConfigGroup config(KGlobal::config(), QString::fromLatin1("ImportOptions - FreeDB"));
+ config.writeEntry("Cache Files Only", true);
+ }
+
+ KCDDB::Config cfg;
+ cfg.readConfig();
+
+ QStringList dirs = cfg.cacheLocations();
+ for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
+ dirs += Tellico::findAllSubDirs(*it);
+ }
+
+ // using a QMap is a lazy man's way of getting unique keys
+ // the cddb info may be in multiple files, all with the same filename, the cddb id
+ QMap<QString, QString> files;
+ for(QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
+ if((*it).isEmpty()) {
+ continue;
+ }
+
+ QDir dir(*it);
+ dir.setFilter(QDir::Files | QDir::Readable | QDir::Hidden); // hidden since I want directory files
+ const QStringList list = dir.entryList();
+ for(QStringList::ConstIterator it2 = list.begin(); it2 != list.end(); ++it2) {
+ files.insert(*it2, dir.absFilePath(*it2), false);
+ }
+// kapp->processEvents(); // really needed ?
+ }
+
+ const QString title = QString::fromLatin1("title");
+ const QString artist = QString::fromLatin1("artist");
+ const QString year = QString::fromLatin1("year");
+ const QString genre = QString::fromLatin1("genre");
+ const QString track = QString::fromLatin1("track");
+ const QString comments = QString::fromLatin1("comments");
+ uint numFiles = files.count();
+
+ if(numFiles == 0) {
+ myDebug() << "FreeDBImporter::readCache() - no files found" << endl;
+ return;
+ }
+
+ m_coll = new Data::MusicCollection(true);
+
+ const uint stepSize = QMAX(1, numFiles / 100);
+ const bool showProgress = options() & ImportProgress;
+
+ ProgressItem& item = ProgressManager::self()->newProgressItem(this, progressLabel(), true);
+ item.setTotalSteps(numFiles);
+ connect(&item, SIGNAL(signalCancelled(ProgressItem*)), SLOT(slotCancel()));
+ ProgressItem::Done done(this);
+
+ uint step = 1;
+
+ KCDDB::CDInfo info;
+ for(QMap<QString, QString>::Iterator it = files.begin(); !m_cancelled && it != files.end(); ++it, ++step) {
+ // open file and read content
+ QFileInfo fileinfo(it.data()); // skip files larger than 10 kB
+ if(!fileinfo.exists() || !fileinfo.isReadable() || fileinfo.size() > 10*1024) {
+ myDebug() << "FreeDBImporter::readCache() - skipping " << it.data() << endl;
+ continue;
+ }
+ QFile file(it.data());
+ if(!file.open(IO_ReadOnly)) {
+ continue;
+ }
+ QTextStream ts(&file);
+ // libkcddb always writes the cache files in utf-8
+ ts.setEncoding(QTextStream::UnicodeUTF8);
+ QString cddbData = ts.read();
+ file.close();
+
+ if(cddbData.isEmpty() || !info.load(cddbData) || !info.isValid()) {
+ myDebug() << "FreeDBImporter::readCache() - Error - CDDB record is not valid" << endl;
+ myDebug() << "FreeDBImporter::readCache() - File = " << it.data() << endl;
+ continue;
+ }
+
+ // create a new entry and set fields
+ Data::EntryPtr entry = new Data::Entry(m_coll);
+ // obviously a CD
+ entry->setField(QString::fromLatin1("medium"), i18n("Compact Disc"));
+ entry->setField(title, info.title);
+ entry->setField(artist, info.artist);
+ entry->setField(genre, info.genre);
+ if(info.year > 0) {
+ entry->setField(QString::fromLatin1("year"), QString::number(info.year));
+ }
+ entry->setField(QString::fromLatin1("keyword"), info.category);
+ QString extd = info.extd;
+ extd.replace('\n', QString::fromLatin1("<br/>"));
+ entry->setField(QString::fromLatin1("comments"), extd);
+
+ // step through trackList
+ QStringList trackList;
+ KCDDB::TrackInfoList t = info.trackInfoList;
+ for(uint i = 0; i < t.count(); ++i) {
+#if KDE_IS_VERSION(3,4,90)
+ trackList << t[i].get(QString::fromLatin1("title")).toString();
+#else
+ trackList << t[i].title;
+#endif
+ }
+ entry->setField(track, trackList.join(QString::fromLatin1("; ")));
+
+#if 0
+ // add CDDB info
+ const QString br = QString::fromLatin1("<br/>");
+ QString comment;
+ if(!info.extd.isEmpty()) {
+ comment.append(info.extd + br);
+ }
+ if(!info.id.isEmpty()) {
+ comment.append(QString::fromLatin1("CDDB-ID: ") + info.id + br);
+ }
+ if(info.length > 0) {
+ comment.append("Length: " + QString::number(info.length) + br);
+ }
+ if(info.revision > 0) {
+ comment.append("Revision: " + QString::number(info.revision) + br);
+ }
+ entry->setField(comments, comment);
+#endif
+
+ // add this entry to the music collection
+ m_coll->addEntries(entry);
+
+ if(showProgress && step%stepSize == 0) {
+ ProgressManager::self()->setProgress(this, step);
+ kapp->processEvents();
+ }
+ }
+#endif
+}
+
+#define SETFIELD(name,value) \
+ if(entry->field(QString::fromLatin1(name)).isEmpty()) { \
+ entry->setField(QString::fromLatin1(name), value); \
+ }
+
+void FreeDBImporter::readCDText(const QCString& drive_) {
+#ifdef USE_CDTEXT
+ Data::EntryPtr entry;
+ if(m_coll) {
+ if(m_coll->entryCount() > 0) {
+ entry = m_coll->entries().front();
+ }
+ } else {
+ m_coll = new Data::MusicCollection(true);
+ }
+ if(!entry) {
+ entry = new Data::Entry(m_coll);
+ entry->setField(QString::fromLatin1("medium"), i18n("Compact Disc"));
+ m_coll->addEntries(entry);
+ }
+
+ CDText cdtext = getCDText(drive_);
+/*
+ myDebug() << "CDText - title: " << cdtext.title << endl;
+ myDebug() << "CDText - title: " << cdtext.artist << endl;
+ for(int i = 0; i < cdtext.trackTitles.size(); ++i) {
+ myDebug() << i << "::" << cdtext.trackTitles[i] << " - " << cdtext.trackArtists[i] << endl;
+ }
+*/
+
+ QString artist = cdtext.artist;
+ SETFIELD("title", cdtext.title);
+ SETFIELD("artist", artist);
+ SETFIELD("comments", cdtext.message);
+ QStringList tracks;
+ for(uint i = 0; i < cdtext.trackTitles.size(); ++i) {
+ tracks << cdtext.trackTitles[i] + "::" + cdtext.trackArtists[i];
+ if(artist.isEmpty()) {
+ artist = cdtext.trackArtists[i];
+ }
+ if(!artist.isEmpty() && artist.lower() != cdtext.trackArtists[i].lower()) {
+ artist = i18n("Various");
+ }
+ }
+ SETFIELD("track", tracks.join(QString::fromLatin1("; ")));
+
+ // something special for compilations and such
+ SETFIELD("title", i18n(Data::Collection::s_emptyGroupTitle));
+ SETFIELD("artist", artist);
+#endif
+}
+#undef SETFIELD
+
+QWidget* FreeDBImporter::widget(QWidget* parent_, const char* name_/*=0*/) {
+ if(m_widget) {
+ return m_widget;
+ }
+ m_widget = new QWidget(parent_, name_);
+ QVBoxLayout* l = new QVBoxLayout(m_widget);
+
+ QGroupBox* bigbox = new QGroupBox(1, Qt::Horizontal, i18n("Audio CD Options"), m_widget);
+
+ // cdrom stuff
+ QHBox* box = new QHBox(bigbox);
+ m_radioCDROM = new QRadioButton(i18n("Read data from CD-ROM device"), box);
+ m_driveCombo = new KComboBox(true, box);
+ m_driveCombo->setDuplicatesEnabled(false);
+ QString w = i18n("Select or input the CD-ROM device location.");
+ QWhatsThis::add(m_radioCDROM, w);
+ QWhatsThis::add(m_driveCombo, w);
+
+ /********************************************************************************/
+
+ m_radioCache = new QRadioButton(i18n("Read all CDDB cache files only"), bigbox);
+ QWhatsThis::add(m_radioCache, i18n("Read data recursively from all the CDDB cache files "
+ "contained in the default cache folders."));
+
+ // cddb cache stuff
+ m_buttonGroup = new QButtonGroup(m_widget);
+ m_buttonGroup->hide(); // only use as button parent
+ m_buttonGroup->setExclusive(true);
+ m_buttonGroup->insert(m_radioCDROM);
+ m_buttonGroup->insert(m_radioCache);
+ connect(m_buttonGroup, SIGNAL(clicked(int)), SLOT(slotClicked(int)));
+
+ l->addWidget(bigbox);
+ l->addStretch(1);
+
+ // now read config options
+ KConfigGroup config(KGlobal::config(), QString::fromLatin1("ImportOptions - FreeDB"));
+ QStringList devices = config.readListEntry("CD-ROM Devices");
+ if(devices.isEmpty()) {
+#if defined(__OpenBSD__)
+ devices += QString::fromLatin1("/dev/rcd0c");
+#endif
+ devices += QString::fromLatin1("/dev/cdrom");
+ devices += QString::fromLatin1("/dev/dvd");
+ }
+ m_driveCombo->insertStringList(devices);
+ QString device = config.readEntry("Last Device");
+ if(!device.isEmpty()) {
+ m_driveCombo->setCurrentText(device);
+ }
+ if(config.readBoolEntry("Cache Files Only", false)) {
+ m_radioCache->setChecked(true);
+ } else {
+ m_radioCDROM->setChecked(true);
+ }
+ // set enabled widgets
+ slotClicked(m_buttonGroup->selectedId());
+
+ return m_widget;
+}
+
+void FreeDBImporter::slotClicked(int id_) {
+ QButton* button = m_buttonGroup->find(id_);
+ if(!button) {
+ return;
+ }
+
+ m_driveCombo->setEnabled(button == m_radioCDROM);
+}
+
+void FreeDBImporter::slotCancel() {
+ m_cancelled = true;
+}
+
+#include "freedbimporter.moc"