Initial support for cover artwork.

This commit is contained in:
LoRd_MuldeR 2011-03-20 23:32:11 +01:00
parent db6b218d5d
commit 157542f730
7 changed files with 178 additions and 1 deletions

View File

@ -25,7 +25,7 @@
#define VER_LAMEXP_MAJOR 4
#define VER_LAMEXP_MINOR_HI 0
#define VER_LAMEXP_MINOR_LO 1
#define VER_LAMEXP_BUILD 382
#define VER_LAMEXP_BUILD 385
#define VER_LAMEXP_SUFFIX Beta-10
/*

View File

@ -115,6 +115,7 @@ bool MP3Encoder::encode(const QString &sourceFile, const AudioFileModel &metaInf
if(!metaInfo.fileComment().isEmpty()) args << (isUnicode(metaInfo.fileComment()) ? "--uComment" : "--lComment") << metaInfo.fileComment();
if(metaInfo.fileYear()) args << "--ty" << QString::number(metaInfo.fileYear());
if(metaInfo.filePosition()) args << "--tn" << QString::number(metaInfo.filePosition());
if(!metaInfo.fileCover().isEmpty()) args << "--ti" << QDir::toNativeSeparators(metaInfo.fileCover());
//args << "--tv" << QString().sprintf("Encoder=LameXP v%d.%02d.%04d [%s]", lamexp_version_major(), lamexp_version_minor(), lamexp_version_build(), lamexp_version_release());

View File

@ -23,6 +23,12 @@
#include <QTime>
#include <QObject>
#include <QMutexLocker>
#include <QFile>
QMutex AudioFileModel::m_mutexCovers;
QMap<QString, unsigned int> AudioFileModel::m_counterCovers;
QMap<QString, QFile*> AudioFileModel::m_locksCovers;
////////////////////////////////////////////////////////////
// Constructor & Destructor
@ -58,6 +64,7 @@ AudioFileModel::AudioFileModel(const AudioFileModel &model, bool copyMetaInfo)
setFileAlbum(model.m_fileAlbum);
setFileGenre(model.m_fileGenre);
setFileComment(model.m_fileComment);
setFileCover(model.m_fileCover);
setFileYear(model.m_fileYear);
setFilePosition(model.m_filePosition);
}
@ -71,6 +78,7 @@ AudioFileModel &AudioFileModel::operator=(const AudioFileModel &model)
setFileAlbum(model.m_fileAlbum);
setFileGenre(model.m_fileGenre);
setFileComment(model.m_fileComment);
setFileCover(model.m_fileCover);
setFileYear(model.m_fileYear);
setFilePosition(model.m_filePosition);
setFileDuration(model.m_fileDuration);
@ -89,6 +97,10 @@ AudioFileModel &AudioFileModel::operator=(const AudioFileModel &model)
AudioFileModel::~AudioFileModel(void)
{
if(!m_fileCover.isEmpty())
{
setFileCover(QString());
}
}
////////////////////////////////////////////////////////////
@ -103,6 +115,7 @@ void AudioFileModel::resetAll(void)
m_fileAlbum.clear();
m_fileGenre.clear();
m_fileComment.clear();
m_fileCover.clear();
m_fileYear = 0;
m_filePosition = 0;
@ -157,6 +170,11 @@ const QString &AudioFileModel::fileComment(void) const
return m_fileComment;
}
const QString &AudioFileModel::fileCover(void) const
{
return m_fileCover;
}
unsigned int AudioFileModel::fileYear(void) const
{
return m_fileYear;
@ -327,6 +345,39 @@ void AudioFileModel::setFileComment(const QString &comment)
m_fileComment = comment;
}
void AudioFileModel::setFileCover(const QString &coverFile)
{
QMutexLocker lock(&m_mutexCovers);
if(m_fileCover.isEmpty() || coverFile.isEmpty() || (m_fileCover.compare(coverFile, Qt::CaseInsensitive) != 0))
{
if(!m_fileCover.isEmpty() && m_counterCovers.contains(m_fileCover))
{
if(--m_counterCovers[m_fileCover] < 1)
{
m_counterCovers.remove(m_fileCover);
if(m_locksCovers.contains(m_fileCover))
{
delete m_locksCovers[m_fileCover];
m_locksCovers.remove(m_fileCover);
}
QFile::remove(m_fileCover);
}
}
if(!coverFile.isEmpty())
{
if(!m_counterCovers.contains(coverFile))
{
m_counterCovers.insert(coverFile, 0);
m_locksCovers.insert(coverFile, new QFile(coverFile));
m_locksCovers[coverFile]->open(QIODevice::ReadOnly);
}
m_counterCovers[coverFile]++;
}
}
m_fileCover = coverFile;
}
void AudioFileModel::setFileYear(unsigned int year)
{
m_fileYear = year;

View File

@ -23,6 +23,10 @@
#include <QObject>
#include <QString>
#include <QMap>
#include <QMutex>
class QFile;
class AudioFileModel : public QObject
{
@ -44,6 +48,7 @@ public:
const QString &fileAlbum(void) const;
const QString &fileGenre(void) const;
const QString &fileComment(void) const;
const QString &fileCover(void) const;
unsigned int fileYear(void) const;
unsigned int filePosition(void) const;
unsigned int fileDuration(void) const;
@ -72,6 +77,7 @@ public:
void setFileAlbum(const QString &album);
void setFileGenre(const QString &genre);
void setFileComment(const QString &comment);
void setFileCover(const QString &coverFile);
void setFileYear(unsigned int year);
void setFilePosition(unsigned int position);
void setFileDuration(unsigned int duration);
@ -94,6 +100,7 @@ private:
QString m_fileAlbum;
QString m_fileGenre;
QString m_fileComment;
QString m_fileCover;
unsigned int m_fileYear;
unsigned int m_filePosition;
unsigned int m_fileDuration;
@ -107,5 +114,9 @@ private:
unsigned int m_formatAudioChannels;
unsigned int m_formatAudioBitdepth;
static QMutex m_mutexCovers;
static QMap<QString, unsigned int> m_counterCovers;
static QMap<QString, QFile*> m_locksCovers;
void resetAll(void);
};

View File

@ -107,6 +107,7 @@ const AudioFileModel FileAnalyzer::analyzeFile(const QString &filePath)
AudioFileModel audioFile(filePath);
m_currentSection = sectionOther;
m_currentCover = coverNone;
QFile readTest(filePath);
if(!readTest.open(QIODevice::ReadOnly))
@ -198,6 +199,11 @@ const AudioFileModel FileAnalyzer::analyzeFile(const QString &filePath)
audioFile.setFileName(baseName);
}
if(m_currentCover != coverNone)
{
retrieveCover(audioFile, filePath, mediaInfoBin);
}
return audioFile;
}
@ -267,6 +273,22 @@ void FileAnalyzer::updateInfo(AudioFileModel &audioFile, const QString &key, con
{
if(audioFile.formatContainerProfile().isEmpty()) audioFile.setFormatContainerProfile(value);
}
else if(!key.compare("Cover MIME", Qt::CaseInsensitive))
{
QString temp = value.split(" ", QString::SkipEmptyParts, Qt::CaseInsensitive).first();
if(!temp.compare("image/jpeg", Qt::CaseInsensitive))
{
m_currentCover = coverJpeg;
}
else if(!temp.compare("image/png", Qt::CaseInsensitive))
{
m_currentCover = coverPng;
}
else if(!temp.compare("image/gif", Qt::CaseInsensitive))
{
m_currentCover = coverGif;
}
}
break;
case sectionAudio:
@ -378,6 +400,86 @@ bool FileAnalyzer::checkFile_CDDA(QFile &file)
return ((i >= 0) && (j >= 0) && (k >= 0) && (k > j) && (j > i));
}
void FileAnalyzer::retrieveCover(AudioFileModel &audioFile, const QString &filePath, const QString &mediaInfoBin)
{
qDebug64("Retrieving cover from: %1", filePath);
QString extension;
switch(m_currentCover)
{
case coverPng:
extension = QString::fromLatin1("png");
break;
case coverGif:
extension = QString::fromLatin1("gif");
break;
default:
extension = QString::fromLatin1("jpg");
break;
}
QProcess process;
process.setProcessChannelMode(QProcess::MergedChannels);
process.setReadChannel(QProcess::StandardOutput);
process.start(mediaInfoBin, QStringList() << "-f" << QDir::toNativeSeparators(filePath));
if(!process.waitForStarted())
{
qWarning("MediaInfo process failed to create!");
qWarning("Error message: \"%s\"\n", process.errorString().toLatin1().constData());
process.kill();
process.waitForFinished(-1);
return;
}
while(process.state() != QProcess::NotRunning)
{
if(!process.waitForReadyRead())
{
if(process.state() == QProcess::Running)
{
qWarning("MediaInfo time out. Killing process and skipping file!");
process.kill();
process.waitForFinished(-1);
return;
}
}
while(process.canReadLine())
{
QString line = QString::fromUtf8(process.readLine().constData()).simplified();
if(!line.isEmpty())
{
int index = line.indexOf(':');
if(index > 0)
{
QString key = line.left(index-1).trimmed();
QString val = line.mid(index+1).trimmed();
if(!key.isEmpty() && !val.isEmpty())
{
if(!key.compare("Cover_Data", Qt::CaseInsensitive))
{
if(val.indexOf(" ") > 0)
{
val = val.split(" ", QString::SkipEmptyParts, Qt::CaseInsensitive).first();
}
QByteArray coverData = QByteArray::fromBase64(val.toLatin1());
QFile coverFile(QString("%1/%2.%3").arg(lamexp_temp_folder2(), lamexp_rand_str(), extension));
if(coverFile.open(QIODevice::WriteOnly))
{
coverFile.write(coverData);
coverFile.close();
audioFile.setFileCover(coverFile.fileName());
}
break;
}
}
}
}
}
}
}
////////////////////////////////////////////////////////////
// Public Functions
////////////////////////////////////////////////////////////

View File

@ -59,6 +59,13 @@ private:
sectionAudio,
sectionOther
};
enum cover_t
{
coverNone,
coverJpeg,
coverPng,
coverGif
};
const AudioFileModel analyzeFile(const QString &filePath);
void updateInfo(AudioFileModel &audioFile, const QString &key, const QString &value);
@ -66,11 +73,13 @@ private:
unsigned int parseYear(const QString &str);
unsigned int parseDuration(const QString &str);
bool checkFile_CDDA(QFile &file);
void retrieveCover(AudioFileModel &audioFile, const QString &filePath, const QString &mediaInfoBin);
QStringList m_inputFiles;
const QString m_mediaInfoBin_x86;
const QString m_mediaInfoBin_x64;
section_t m_currentSection;
cover_t m_currentCover;
unsigned int m_filesAccepted;
unsigned int m_filesRejected;
unsigned int m_filesDenied;

View File

@ -36,6 +36,7 @@
#include <QDir>
#include <QMutex>
#include <QMutexLocker>
#include <QDate>
#include <limits.h>
#include <time.h>
@ -105,6 +106,8 @@ void ProcessThread::processFile()
qDebug("Process thread %s has started.", m_jobId.toString().toLatin1().constData());
emit processStateInitialized(m_jobId, QFileInfo(m_audioFile.filePath()).fileName(), tr("Starting..."), ProgressModel::JobRunning);
handleMessage(QString().sprintf("LameXP v%u.%02u (Build #%u), compiled at %s", lamexp_version_major(), lamexp_version_minor(), lamexp_version_build(), lamexp_version_date().toString(Qt::ISODate).toLatin1().constData()));
handleMessage("\n-------------------------------\n");
//Generate output file name
QString outFileName = generateOutFileName();