Commit 2992af1d authored by Urs Fleisch's avatar Urs Fleisch
Browse files

option to import cover art from "Import from Discogs" and "Import from MusicBrainz Release"

parent 1c255654
......@@ -6,7 +6,7 @@
* \author Urs Fleisch
* \date 13 Oct 2006
*
* Copyright (C) 2006-2007 Urs Fleisch
* Copyright (C) 2006-2009 Urs Fleisch
*
* This file is part of Kid3.
*
......@@ -388,6 +388,21 @@ void DiscogsDialog::parseAlbumResults(const QByteArray& albumStr)
}
}
m_trackDataVector.setCoverArtUrl(QString::null);
if (getCoverArt()) {
/*
* cover art can be found in image source
*/
start = str.QCM_indexOf("<img src=\"http://www.discogs.com/image/");
if (start >= 0) {
start += 10; // skip <img src="
end = str.QCM_indexOf("\"", start);
if (end > start) {
m_trackDataVector.setCoverArtUrl(str.mid(start, end - start));
}
}
}
/*
* album tracks have the format (lines with only whitespace in between):
<b>Tracklisting:</b><br>
......
......@@ -45,7 +45,7 @@ ImportSourceConfig::ImportSourceConfig(const QString& grp, bool cgiPathUsed,
bool additionalTagsUsed) :
GeneralConfig(grp), m_windowWidth(-1), m_windowHeight(-1),
m_cgiPathUsed(cgiPathUsed), m_additionalTagsUsed(additionalTagsUsed),
m_additionalTags(true)
m_additionalTags(true), m_coverArt(true)
{
}
......@@ -78,8 +78,10 @@ void ImportSourceConfig::writeToConfig(
cfg.writeEntry("Server", m_server);
if (m_cgiPathUsed)
cfg.writeEntry("CgiPath", m_cgiPath);
if (m_additionalTagsUsed)
if (m_additionalTagsUsed) {
cfg.writeEntry("AdditionalTags", m_additionalTags);
cfg.writeEntry("CoverArt", m_coverArt);
}
cfg.writeEntry("WindowWidth", m_windowWidth);
cfg.writeEntry("WindowHeight", m_windowHeight);
#else
......@@ -87,8 +89,10 @@ void ImportSourceConfig::writeToConfig(
config->QCM_writeEntry("/Server", m_server);
if (m_cgiPathUsed)
config->QCM_writeEntry("/CgiPath", m_cgiPath);
if (m_additionalTagsUsed)
if (m_additionalTagsUsed) {
config->QCM_writeEntry("/AdditionalTags", m_additionalTags);
config->QCM_writeEntry("/CoverArt", m_coverArt);
}
config->QCM_writeEntry("/WindowWidth", m_windowWidth);
config->QCM_writeEntry("/WindowHeight", m_windowHeight);
config->endGroup();
......@@ -113,9 +117,11 @@ void ImportSourceConfig::readFromConfig(
m_server = cfg.readEntry("Server", m_server);
if (m_cgiPathUsed)
m_cgiPath = cfg.readEntry("CgiPath", m_cgiPath);
if (m_additionalTagsUsed)
if (m_additionalTagsUsed) {
m_additionalTags = cfg.KCM_readBoolEntry("AdditionalTags",
m_additionalTags);
m_coverArt = cfg.KCM_readBoolEntry("CoverArt", m_coverArt);
}
m_windowWidth = cfg.KCM_readNumEntry("WindowWidth", -1);
m_windowHeight = cfg.KCM_readNumEntry("WindowHeight", -1);
#else
......@@ -123,9 +129,11 @@ void ImportSourceConfig::readFromConfig(
m_server = config->QCM_readEntry("/Server", m_server);
if (m_cgiPathUsed)
m_cgiPath = config->QCM_readEntry("/CgiPath", m_cgiPath);
if (m_additionalTagsUsed)
if (m_additionalTagsUsed) {
m_additionalTags = config->QCM_readBoolEntry("/AdditionalTags",
m_additionalTags);
m_coverArt = config->QCM_readBoolEntry("/CoverArt", m_coverArt);
}
m_windowWidth = config->QCM_readNumEntry("/WindowWidth", -1);
m_windowHeight = config->QCM_readNumEntry("/WindowHeight", -1);
config->endGroup();
......
......@@ -103,6 +103,9 @@ public:
/** additional tags imported */
bool m_additionalTags;
/** cover art imported */
bool m_coverArt;
};
#endif
......@@ -56,7 +56,7 @@ ImportSourceDialog::ImportSourceDialog(QWidget* parent, QString caption,
const Properties& props)
: QDialog(parent), m_trackDataVector(trackDataVector),
m_serverComboBox(0), m_cgiLineEdit(0), m_additionalTagsCheckBox(0),
m_client(client), m_props(props)
m_coverArtCheckBox(0), m_client(client), m_props(props)
{
setModal(true);
QCM_setWindowTitle(caption);
......@@ -118,8 +118,14 @@ ImportSourceDialog::ImportSourceDialog(QWidget* parent, QString caption,
}
}
if (m_props.additionalTags) {
QHBoxLayout* hlayout = new QHBoxLayout;
m_additionalTagsCheckBox = new QCheckBox(i18n("&Additional Tags"), this);
vlayout->addWidget(m_additionalTagsCheckBox);
m_coverArtCheckBox = new QCheckBox(i18n("C&over Art"), this);
if (hlayout && m_additionalTagsCheckBox && m_coverArtCheckBox) {
hlayout->addWidget(m_additionalTagsCheckBox);
hlayout->addWidget(m_coverArtCheckBox);
vlayout->addLayout(hlayout);
}
}
#if QT_VERSION >= 0x040000
m_albumListBox = new QListWidget(this);
......@@ -306,6 +312,39 @@ void ImportSourceDialog::setAdditionalTags(bool enable)
}
}
/**
* Get cover art option.
*
* @return true if cover art are enabled.
*/
bool ImportSourceDialog::getCoverArt() const
{
return m_coverArtCheckBox ?
#if QT_VERSION >= 0x040000
m_coverArtCheckBox->checkState() == Qt::Checked
#else
m_coverArtCheckBox->isChecked()
#endif
: false;
}
/**
* Set cover art option.
*
* @param enable true if cover art are enabled
*/
void ImportSourceDialog::setCoverArt(bool enable)
{
if (m_coverArtCheckBox) {
#if QT_VERSION >= 0x040000
m_coverArtCheckBox->setCheckState(
enable ? Qt::Checked : Qt::Unchecked);
#else
m_coverArtCheckBox->setChecked(enable);
#endif
}
}
/**
* Get the local configuration.
*
......@@ -316,6 +355,7 @@ void ImportSourceDialog::getImportSourceConfig(ImportSourceConfig* cfg) const
cfg->m_server = getServer();
cfg->m_cgiPath = getCgiPath();
cfg->m_additionalTags = getAdditionalTags();
cfg->m_coverArt = getCoverArt();
cfg->m_windowWidth = size().width();
cfg->m_windowHeight = size().height();
}
......@@ -342,6 +382,7 @@ void ImportSourceDialog::setArtistAlbum(const QString& artist, const QString& al
setServer(m_props.cfg->m_server);
setCgiPath(m_props.cfg->m_cgiPath);
setAdditionalTags(m_props.cfg->m_additionalTags);
setCoverArt(m_props.cfg->m_coverArt);
if (m_props.cfg->m_windowWidth > 0 && m_props.cfg->m_windowHeight > 0) {
resize(m_props.cfg->m_windowWidth, m_props.cfg->m_windowHeight);
}
......
......@@ -148,6 +148,20 @@ public:
*/
void setAdditionalTags(bool enable);
/**
* Get cover art option.
*
* @return true if cover art are enabled.
*/
bool getCoverArt() const;
/**
* Set cover art option.
*
* @param enable true if cover art are enabled
*/
void setCoverArt(bool enable);
/**
* Set a find string from artist and album information.
*
......@@ -239,6 +253,7 @@ private:
QComboBox* m_serverComboBox;
QLineEdit* m_cgiLineEdit;
QCheckBox* m_additionalTagsCheckBox;
QCheckBox* m_coverArtCheckBox;
QStatusBar* m_statusBar;
ImportSourceClient* m_client;
const Properties& m_props;
......
......@@ -223,9 +223,22 @@ public:
*/
void setAlbum(const QString& album) { m_album = album; }
/**
* Get cover art URL.
* @return cover art URL.
*/
QString getCoverArtUrl() const { return m_coverArtUrl; }
/**
* Set cover art URL.
* @param coverArtUrl cover art URL
*/
void setCoverArtUrl(const QString& coverArtUrl) { m_coverArtUrl = coverArtUrl; }
private:
QString m_artist;
QString m_album;
QString m_coverArtUrl;
};
......
......@@ -242,6 +242,7 @@ QString Kid3App::s_dirName;
* Constructor.
*/
Kid3App::Kid3App() :
m_downloadToAllFilesInDir(false),
m_importDialog(0), m_browseCoverArtDialog(0),
m_exportDialog(0), m_renDirDialog(0),
m_numberTracksDialog(0), m_filterDialog(0), m_downloadDialog(0)
......@@ -1845,6 +1846,11 @@ void Kid3App::getTagsFromImportDialog(bool destV1, bool destV2)
}
slotStatusMsg(i18n("Ready."));
QApplication::restoreOverrideCursor();
if (destV2 && flt.isEnabled(Frame::FT_Picture) &&
!m_trackDataList.getCoverArtUrl().isEmpty()) {
downloadImage(m_trackDataList.getCoverArtUrl(), true);
}
}
/**
......@@ -2756,13 +2762,14 @@ void Kid3App::dropImage(const QImage& image)
}
/**
* Handle URL on drop.
* Download an image file.
*
* @param txt dropped URL.
* @param url URL of image
* @param allFilesInDir true to add the image to all files in the directory
*/
void Kid3App::dropUrl(const QString& txt)
void Kid3App::downloadImage(const QString& url, bool allFilesInDir)
{
QString imgurl(BrowseCoverArtDialog::getImageUrl(txt));
QString imgurl(BrowseCoverArtDialog::getImageUrl(url));
if (!imgurl.isEmpty()) {
if (!m_downloadDialog) {
m_downloadDialog = new DownloadDialog(0, i18n("Download"));
......@@ -2774,6 +2781,7 @@ void Kid3App::dropUrl(const QString& txt)
if (hostPos > 0) {
int pathPos = imgurl.QCM_indexOf("/", hostPos + 3);
if (pathPos > hostPos) {
m_downloadToAllFilesInDir = allFilesInDir;
m_downloadDialog->startDownload(
imgurl.mid(hostPos + 3, pathPos - hostPos - 3),
imgurl.mid(pathPos));
......@@ -2784,6 +2792,16 @@ void Kid3App::dropUrl(const QString& txt)
}
}
/**
* Handle URL on drop.
*
* @param txt dropped URL.
*/
void Kid3App::dropUrl(const QString& txt)
{
downloadImage(txt, false);
}
/**
* Add a downloaded image.
*
......@@ -2795,7 +2813,18 @@ void Kid3App::imageDownloaded(const QByteArray& data,
const QString& mimeType, const QString& url)
{
PictureFrame frame(data, url, PictureFrame::PT_CoverFront, mimeType);
addFrame(&frame);
if (m_downloadToAllFilesInDir) {
FileListItem* mp3file = m_view->firstFileInDir();
while (mp3file != 0) {
TaggedFile* taggedFile = mp3file->getFile();
taggedFile->readTags(false);
taggedFile->addFrameV2(frame);
mp3file = m_view->nextFileInDir();
}
m_downloadToAllFilesInDir = false;
} else {
addFrame(&frame);
}
updateGuiControls();
}
......
......@@ -197,6 +197,14 @@ public:
*/
void dropImage(const QImage& image);
/**
* Download an image file.
*
* @param url URL of image
* @param allFilesInDir true to add the image to all files in the directory
*/
void downloadImage(const QString& url, bool allFilesInDir);
/**
* Handle URL on drop.
*
......@@ -746,6 +754,8 @@ private:
bool m_modified;
/** true if list is filtered */
bool m_filtered;
/** true to add frame to all files in directory in imageDownloaded() */
bool m_downloadToAllFilesInDir;
/** Copy buffer */
FrameCollection m_copyTags;
/** Import dialog */
......
......@@ -86,5 +86,8 @@ void MusicBrainzReleaseClient::sendTrackListQuery(
path += "+release-events+artist-rels+release-rels+track-rels+"
"track-level-rels+labels";
}
if (cfg->m_coverArt) {
path += "+url-rels";
}
sendRequest(cfg->m_server, path);
}
......@@ -164,61 +164,58 @@ static void addInvolvedPeople(
/**
* Set tags from an XML node with a relation list.
*
* @param node parent DOM node
* @param frames tags will be added to these frames
* @param relationList relation-list with target-type Artist
* @param frames tags will be added to these frames
*
* @return true if credits found.
*/
static bool parseCredits(const QDomNode& node, FrameCollection& frames)
static bool parseCredits(const QDomElement& relationList, FrameCollection& frames)
{
bool result = false;
QDomElement relationList(node.namedItem("relation-list").toElement());
if (!relationList.isNull() && relationList.attribute("target-type") == "Artist") {
QDomNode relation(relationList.firstChild());
while (!relation.isNull()) {
QString artist(relation.toElement().namedItem("artist").toElement().
namedItem("name").toElement().text());
if (!artist.isEmpty()) {
QString type(relation.toElement().attribute("type"));
if (type == "Instrument") {
QString attributes(relation.toElement().attribute("attributes"));
if (!attributes.isEmpty()) {
addInvolvedPeople(frames, Frame::FT_Performer,
fixUpCamelCase(attributes), artist);
}
} else if (type == "Vocal") {
addInvolvedPeople(frames, Frame::FT_Performer, type, artist);
} else {
static const struct {
const char* credit;
Frame::Type type;
} creditToType[] = {
{ "Composer", Frame::FT_Composer },
{ "Conductor", Frame::FT_Conductor },
{ "PerformingOrchestra", Frame::FT_AlbumArtist },
{ "Lyricist", Frame::FT_Lyricist },
{ "Publisher", Frame::FT_Publisher },
{ "Remixer", Frame::FT_Remixer }
};
bool found = false;
for (unsigned i = 0;
i < sizeof(creditToType) / sizeof(creditToType[0]);
++i) {
if (type == creditToType[i].credit) {
frames.setValue(creditToType[i].type, artist);
found = true;
break;
}
}
if (!found && type != "Tribute") {
addInvolvedPeople(frames, Frame::FT_Arranger,
fixUpCamelCase(type), artist);
QDomNode relation(relationList.firstChild());
while (!relation.isNull()) {
QString artist(relation.toElement().namedItem("artist").toElement().
namedItem("name").toElement().text());
if (!artist.isEmpty()) {
QString type(relation.toElement().attribute("type"));
if (type == "Instrument") {
QString attributes(relation.toElement().attribute("attributes"));
if (!attributes.isEmpty()) {
addInvolvedPeople(frames, Frame::FT_Performer,
fixUpCamelCase(attributes), artist);
}
} else if (type == "Vocal") {
addInvolvedPeople(frames, Frame::FT_Performer, type, artist);
} else {
static const struct {
const char* credit;
Frame::Type type;
} creditToType[] = {
{ "Composer", Frame::FT_Composer },
{ "Conductor", Frame::FT_Conductor },
{ "PerformingOrchestra", Frame::FT_AlbumArtist },
{ "Lyricist", Frame::FT_Lyricist },
{ "Publisher", Frame::FT_Publisher },
{ "Remixer", Frame::FT_Remixer }
};
bool found = false;
for (unsigned i = 0;
i < sizeof(creditToType) / sizeof(creditToType[0]);
++i) {
if (type == creditToType[i].credit) {
frames.setValue(creditToType[i].type, artist);
found = true;
break;
}
}
if (!found && type != "Tribute") {
addInvolvedPeople(frames, Frame::FT_Arranger,
fixUpCamelCase(type), artist);
}
}
result = true;
relation = relation.nextSibling();
}
result = true;
relation = relation.nextSibling();
}
return result;
}
......@@ -266,6 +263,16 @@ void MusicBrainzReleaseDialog::parseAlbumResults(const QByteArray& albumStr)
framesHdr.setAlbum(release.namedItem("title").toElement().text());
framesHdr.setArtist(release.namedItem("artist").toElement().namedItem("name").toElement().text());
m_trackDataVector.setCoverArtUrl(QString::null);
const bool coverArt = getCoverArt();
if (coverArt) {
QString asin(release.namedItem("asin").toElement().text());
if (!asin.isEmpty()) {
m_trackDataVector.setCoverArtUrl(
QString("http://www.amazon.com/dp/") + asin);
}
}
const bool additionalTags = getAdditionalTags();
if (additionalTags) {
// release year and label can be found in the release-event-list
......@@ -290,8 +297,41 @@ void MusicBrainzReleaseDialog::parseAlbumResults(const QByteArray& albumStr)
}
}
}
}
parseCredits(release, framesHdr);
if (additionalTags || coverArt) {
QDomNode relationListNode(release.firstChild());
while (!relationListNode.isNull()) {
if (relationListNode.nodeName() == "relation-list") {
QDomElement relationList(relationListNode.toElement());
if (!relationList.isNull()) {
QString targetType(relationList.attribute("target-type"));
if (targetType == "Artist") {
if (additionalTags) {
parseCredits(relationList, framesHdr);
}
} else if (targetType == "Url") {
if (coverArt) {
QDomNode relationNode(relationList.firstChild());
while (!relationNode.isNull()) {
if (relationNode.nodeName() == "relation") {
QDomElement relation(relationNode.toElement());
if (!relation.isNull()) {
QString type(relation.attribute("type"));
if (type == "CoverArtLink" || type == "AmazonAsin") {
m_trackDataVector.setCoverArtUrl(
relation.attribute("target"));
}
}
}
relationNode = relationNode.nextSibling();
}
}
}
}
}
relationListNode = relationListNode.nextSibling();
}
}
ImportTrackDataVector::iterator it = m_trackDataVector.begin();
......@@ -315,7 +355,18 @@ void MusicBrainzReleaseDialog::parseAlbumResults(const QByteArray& albumStr)
frames.setArtist(artist);
frames.setValue(Frame::FT_AlbumArtist, framesHdr.getArtist());
}
parseCredits(trackNode, frames);
QDomNode relationListNode(trackNode.firstChild());
while (!relationListNode.isNull()) {
if (relationListNode.nodeName() == "relation-list") {
QDomElement relationList(relationListNode.toElement());
if (!relationList.isNull()) {
if (relationList.attribute("target-type") == "Artist") {
parseCredits(relationList, frames);
}
}
}
relationListNode = relationListNode.nextSibling();
}
}
if (atTrackDataListEnd) {
ImportTrackData trackData;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment