You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/translators/freedbimporter.cpp

557 lines
17 KiB

/***************************************************************************
copyright : (C) 2004-2006 by Robby Stephenson
email : robby@periapsis.org
***************************************************************************/
/***************************************************************************
* *
* 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 TQT_NO_CAST_ASCII
#define HAD_TQT_NO_CAST_ASCII
#undef TQT_NO_CAST_ASCII
#endif
#include <libkcddb/client.h>
#ifdef HAD_TQT_NO_CAST_ASCII
#define TQT_NO_CAST_ASCII
#undef HAD_TQT_NO_CAST_ASCII
#endif
#endif
#include <kcombobox.h>
#include <tdeconfig.h>
#include <tdeapplication.h>
#include <kinputdialog.h>
#include <tqfile.h>
#include <tqdir.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqgroupbox.h>
#include <tqwhatsthis.h>
#include <tqradiobutton.h>
#include <tqbuttongroup.h>
#include <tqhbox.h>
#include <tqcheckbox.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
TQString 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);
TQStringList drives;
for(int i = 0; i < m_driveCombo->count(); ++i) {
if(drives.findIndex(m_driveCombo->text(i)) == -1) {
drives += m_driveCombo->text(i);
}
}
{
TDEConfigGroup config(TDEGlobal::config(), TQString::fromLatin1("ImportOptions - FreeDB"));
config.writeEntry("CD-ROM Devices", drives);
config.writeEntry("Last Device", drivePath);
config.writeEntry("Cache Files Only", false);
}
TQCString drive = TQFile::encodeName(drivePath);
TQValueList<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) {
TQStringList list;
KCDDB::CDInfoList infoList = client.lookupResponse();
for(KCDDB::CDInfoList::iterator it = infoList.begin(); it != infoList.end(); ++it) {
list.append(TQString::fromLatin1("%1, %2, %3").arg((*it).artist)
.arg((*it).title)
.arg((*it).genre));
}
// switch back to pointer cursor
GUI::CursorSaver cs(TQt::arrowCursor);
bool ok;
TQString 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(TQStringList::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;
TQString 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(TQString::fromLatin1("medium"), i18n("Compact Disc"));
entry->setField(TQString::fromLatin1("title"), info.title);
entry->setField(TQString::fromLatin1("artist"), info.artist);
entry->setField(TQString::fromLatin1("genre"), info.genre);
if(info.year > 0) {
entry->setField(TQString::fromLatin1("year"), TQString::number(info.year));
}
entry->setField(TQString::fromLatin1("keyword"), info.category);
TQString extd = info.extd;
extd.replace('\n', TQString::fromLatin1("<br/>"));
entry->setField(TQString::fromLatin1("comments"), extd);
TQStringList trackList;
KCDDB::TrackInfoList t = info.trackInfoList;
for(uint i = 0; i < t.count(); ++i) {
#if KDE_IS_VERSION(3,4,90)
TQString s = t[i].get(TQString::fromLatin1("title")).toString() + "::" + info.artist;
#else
TQString 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(TQString::fromLatin1("track"), trackList.join(TQString::fromLatin1("; ")));
m_coll->addEntries(entry);
readCDText(drive);
#endif
}
void FreeDBImporter::readCache() {
#ifdef HAVE_KCDDB
{
// remember the import options
TDEConfigGroup config(TDEGlobal::config(), TQString::fromLatin1("ImportOptions - FreeDB"));
config.writeEntry("Cache Files Only", true);
}
KCDDB::Config cfg;
cfg.readConfig();
TQStringList dirs = cfg.cacheLocations();
for(TQStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
dirs += Tellico::findAllSubDirs(*it);
}
// using a TQMap 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
TQMap<TQString, TQString> files;
for(TQStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) {
if((*it).isEmpty()) {
continue;
}
TQDir dir(*it);
dir.setFilter(TQDir::Files | TQDir::Readable | TQDir::Hidden); // hidden since I want directory files
const TQStringList list = dir.entryList();
for(TQStringList::ConstIterator it2 = list.begin(); it2 != list.end(); ++it2) {
files.insert(*it2, dir.absFilePath(*it2), false);
}
// kapp->processEvents(); // really needed ?
}
const TQString title = TQString::fromLatin1("title");
const TQString artist = TQString::fromLatin1("artist");
const TQString year = TQString::fromLatin1("year");
const TQString genre = TQString::fromLatin1("genre");
const TQString track = TQString::fromLatin1("track");
const TQString comments = TQString::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 = TQMAX(1, numFiles / 100);
const bool showProgress = options() & ImportProgress;
ProgressItem& item = ProgressManager::self()->newProgressItem(this, progressLabel(), true);
item.setTotalSteps(numFiles);
connect(&item, TQ_SIGNAL(signalCancelled(ProgressItem*)), TQ_SLOT(slotCancel()));
ProgressItem::Done done(this);
uint step = 1;
KCDDB::CDInfo info;
for(TQMap<TQString, TQString>::Iterator it = files.begin(); !m_cancelled && it != files.end(); ++it, ++step) {
// open file and read content
TQFileInfo 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;
}
TQFile file(it.data());
if(!file.open(IO_ReadOnly)) {
continue;
}
TQTextStream ts(&file);
// libkcddb always writes the cache files in utf-8
ts.setEncoding(TQTextStream::UnicodeUTF8);
TQString 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(TQString::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(TQString::fromLatin1("year"), TQString::number(info.year));
}
entry->setField(TQString::fromLatin1("keyword"), info.category);
TQString extd = info.extd;
extd.replace('\n', TQString::fromLatin1("<br/>"));
entry->setField(TQString::fromLatin1("comments"), extd);
// step through trackList
TQStringList 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(TQString::fromLatin1("title")).toString();
#else
trackList << t[i].title;
#endif
}
entry->setField(track, trackList.join(TQString::fromLatin1("; ")));
#if 0
// add CDDB info
const TQString br = TQString::fromLatin1("<br/>");
TQString comment;
if(!info.extd.isEmpty()) {
comment.append(info.extd + br);
}
if(!info.id.isEmpty()) {
comment.append(TQString::fromLatin1("CDDB-ID: ") + info.id + br);
}
if(info.length > 0) {
comment.append("Length: " + TQString::number(info.length) + br);
}
if(info.revision > 0) {
comment.append("Revision: " + TQString::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(TQString::fromLatin1(name)).isEmpty()) { \
entry->setField(TQString::fromLatin1(name), value); \
}
void FreeDBImporter::readCDText(const TQCString& 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(TQString::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;
}
*/
TQString artist = cdtext.artist;
SETFIELD("title", cdtext.title);
SETFIELD("artist", artist);
SETFIELD("comments", cdtext.message);
TQStringList 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(TQString::fromLatin1("; ")));
// something special for compilations and such
SETFIELD("title", i18n(Data::Collection::s_emptyGroupTitle));
SETFIELD("artist", artist);
#endif
}
#undef SETFIELD
TQWidget* FreeDBImporter::widget(TQWidget* parent_, const char* name_/*=0*/) {
if(m_widget) {
return m_widget;
}
m_widget = new TQWidget(parent_, name_);
TQVBoxLayout* l = new TQVBoxLayout(m_widget);
TQGroupBox* bigbox = new TQGroupBox(1, TQt::Horizontal, i18n("Audio CD Options"), m_widget);
// cdrom stuff
TQHBox* box = new TQHBox(bigbox);
m_radioCDROM = new TQRadioButton(i18n("Read data from CD-ROM device"), box);
m_driveCombo = new KComboBox(true, box);
m_driveCombo->setDuplicatesEnabled(false);
TQString w = i18n("Select or input the CD-ROM device location.");
TQWhatsThis::add(m_radioCDROM, w);
TQWhatsThis::add(m_driveCombo, w);
/********************************************************************************/
m_radioCache = new TQRadioButton(i18n("Read all CDDB cache files only"), bigbox);
TQWhatsThis::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 TQButtonGroup(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, TQ_SIGNAL(clicked(int)), TQ_SLOT(slotClicked(int)));
l->addWidget(bigbox);
l->addStretch(1);
// now read config options
TDEConfigGroup config(TDEGlobal::config(), TQString::fromLatin1("ImportOptions - FreeDB"));
TQStringList devices = config.readListEntry("CD-ROM Devices");
if(devices.isEmpty()) {
#if defined(__OpenBSD__)
devices += TQString::fromLatin1("/dev/rcd0c");
#endif
devices += TQString::fromLatin1("/dev/cdrom");
devices += TQString::fromLatin1("/dev/dvd");
}
m_driveCombo->insertStringList(devices);
TQString 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_) {
TQButton* button = m_buttonGroup->find(id_);
if(!button) {
return;
}
m_driveCombo->setEnabled(button == m_radioCDROM);
}
void FreeDBImporter::slotCancel() {
m_cancelled = true;
}
#include "freedbimporter.moc"