Initial support for cover artwork.
This commit is contained in:
parent
db6b218d5d
commit
157542f730
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
////////////////////////////////////////////////////////////
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user