1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2010 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "Thread_FileAnalyzer.h"
25 #include "LockedFile.h"
26 #include "Model_AudioFile.h"
37 ////////////////////////////////////////////////////////////
39 ////////////////////////////////////////////////////////////
41 FileAnalyzer::FileAnalyzer(const QStringList &inputFiles)
43 m_inputFiles(inputFiles),
44 m_mediaInfoBin_x86(lamexp_lookup_tool("mediainfo_i386.exe")),
45 m_mediaInfoBin_x64(lamexp_lookup_tool("mediainfo_x64.exe"))
49 if(m_mediaInfoBin_x86.isEmpty() || m_mediaInfoBin_x64.isEmpty())
51 qFatal("Invalid path to MediaInfo binary. Tool not initialized properly.");
59 ////////////////////////////////////////////////////////////
61 ////////////////////////////////////////////////////////////
63 void FileAnalyzer::run()
73 while(!m_inputFiles.isEmpty())
75 QString currentFile = QDir::fromNativeSeparators(m_inputFiles.takeFirst());
76 qDebug("Analyzing: %s", currentFile.toUtf8().constData());
77 emit fileSelected(QFileInfo(currentFile).fileName());
78 AudioFileModel file = analyzeFile(currentFile);
79 if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty())
82 qDebug("Skipped: %s", file.filePath().toUtf8().constData());
86 emit fileAnalyzed(file);
89 qDebug("All files added.\n");
93 ////////////////////////////////////////////////////////////
95 ////////////////////////////////////////////////////////////
97 const AudioFileModel FileAnalyzer::analyzeFile(const QString &filePath)
99 lamexp_cpu_t cpuInfo = lamexp_detect_cpu_features();
100 const QString mediaInfoBin = cpuInfo.x64 ? m_mediaInfoBin_x64 : m_mediaInfoBin_x86;
102 AudioFileModel audioFile(filePath);
103 m_currentSection = sectionOther;
105 QFile readTest(filePath);
106 if(!readTest.open(QIODevice::ReadOnly))
108 qWarning("Cannot access file for reading, skipping!");
118 process.setProcessChannelMode(QProcess::MergedChannels);
119 process.setReadChannel(QProcess::StandardOutput);
120 process.start(mediaInfoBin, QStringList() << QDir::toNativeSeparators(filePath));
122 if(!process.waitForStarted())
124 qWarning("MediaInfo process failed to create!");
125 qWarning("Error message: \"%s\"\n", process.errorString().toLatin1().constData());
127 process.waitForFinished(-1);
131 while(process.state() != QProcess::NotRunning)
133 if(!process.waitForReadyRead())
135 if(process.state() == QProcess::Running)
137 qWarning("MediaInfo time out. Killing process and skipping file!");
139 process.waitForFinished(-1);
146 while(process.canReadLine())
148 QString line = QString::fromUtf8(process.readLine().constData()).simplified();
151 int index = line.indexOf(':');
154 QString key = line.left(index-1).trimmed();
155 QString val = line.mid(index+1).trimmed();
156 if(!key.isEmpty() && !val.isEmpty())
158 updateInfo(audioFile, key, val);
169 if(audioFile.fileName().isEmpty())
171 QString baseName = QFileInfo(filePath).fileName();
172 int index = baseName.lastIndexOf(".");
176 baseName = baseName.left(index);
179 baseName = baseName.replace("_", " ").simplified();
180 index = baseName.lastIndexOf(" - ");
184 baseName = baseName.mid(index + 3).trimmed();
187 audioFile.setFileName(baseName);
193 void FileAnalyzer::updateSection(const QString §ion)
195 if(section.startsWith("General", Qt::CaseInsensitive))
197 m_currentSection = sectionGeneral;
199 else if(!section.compare("Audio", Qt::CaseInsensitive) || section.startsWith("Audio #1", Qt::CaseInsensitive))
201 m_currentSection = sectionAudio;
203 else if(section.startsWith("Audio", Qt::CaseInsensitive) || section.startsWith("Video", Qt::CaseInsensitive) || section.startsWith("Text", Qt::CaseInsensitive) ||
204 section.startsWith("Menu", Qt::CaseInsensitive) || section.startsWith("Image", Qt::CaseInsensitive) || section.startsWith("Chapters", Qt::CaseInsensitive))
206 m_currentSection = sectionOther;
210 qWarning("Unknown section: %s", section.toUtf8().constData());
214 void FileAnalyzer::updateInfo(AudioFileModel &audioFile, const QString &key, const QString &value)
216 switch(m_currentSection)
219 if(!key.compare("Title", Qt::CaseInsensitive) || !key.compare("Track", Qt::CaseInsensitive) || !key.compare("Track Name", Qt::CaseInsensitive))
221 if(audioFile.fileName().isEmpty()) audioFile.setFileName(value);
223 else if(!key.compare("Duration", Qt::CaseInsensitive))
225 if(!audioFile.fileDuration()) audioFile.setFileDuration(parseDuration(value));
227 else if(!key.compare("Artist", Qt::CaseInsensitive) || !key.compare("Performer", Qt::CaseInsensitive))
229 if(audioFile.fileArtist().isEmpty()) audioFile.setFileArtist(value);
231 else if(!key.compare("Album", Qt::CaseInsensitive))
233 if(audioFile.fileAlbum().isEmpty()) audioFile.setFileAlbum(value);
235 else if(!key.compare("Genre", Qt::CaseInsensitive))
237 if(audioFile.fileGenre().isEmpty()) audioFile.setFileGenre(value);
239 else if(!key.compare("Year", Qt::CaseInsensitive) || !key.compare("Recorded Date", Qt::CaseInsensitive) || !key.compare("Encoded Date", Qt::CaseInsensitive))
241 if(!audioFile.fileYear()) audioFile.setFileYear(parseYear(value));
243 else if(!key.compare("Comment", Qt::CaseInsensitive))
245 if(audioFile.fileComment().isEmpty()) audioFile.setFileComment(value);
247 else if(!key.compare("Track Name/Position", Qt::CaseInsensitive))
249 if(!audioFile.filePosition()) audioFile.setFilePosition(value.toInt());
251 else if(!key.compare("Format", Qt::CaseInsensitive))
253 if(audioFile.formatContainerType().isEmpty()) audioFile.setFormatContainerType(value);
255 else if(!key.compare("Format Profile", Qt::CaseInsensitive))
257 if(audioFile.formatContainerProfile().isEmpty()) audioFile.setFormatContainerProfile(value);
262 if(!key.compare("Year", Qt::CaseInsensitive) || !key.compare("Recorded Date", Qt::CaseInsensitive) || !key.compare("Encoded Date", Qt::CaseInsensitive))
264 if(!audioFile.fileYear()) audioFile.setFileYear(parseYear(value));
266 else if(!key.compare("Format", Qt::CaseInsensitive))
268 if(audioFile.formatAudioType().isEmpty()) audioFile.setFormatAudioType(value);
270 else if(!key.compare("Format Profile", Qt::CaseInsensitive))
272 if(audioFile.formatAudioProfile().isEmpty()) audioFile.setFormatAudioProfile(value);
274 else if(!key.compare("Format Version", Qt::CaseInsensitive))
276 if(audioFile.formatAudioVersion().isEmpty()) audioFile.setFormatAudioVersion(value);
278 else if(!key.compare("Channel(s)", Qt::CaseInsensitive))
280 if(!audioFile.formatAudioChannels()) audioFile.setFormatAudioChannels(value.split(" ", QString::SkipEmptyParts).first().toInt());
282 else if(!key.compare("Sampling rate", Qt::CaseInsensitive))
284 if(!audioFile.formatAudioSamplerate()) audioFile.setFormatAudioSamplerate(ceil(value.split(" ", QString::SkipEmptyParts).first().toFloat() * 1000.0f));
286 else if(!key.compare("Bit depth", Qt::CaseInsensitive))
288 if(!audioFile.formatAudioBitdepth()) audioFile.setFormatAudioBitdepth(value.split(" ", QString::SkipEmptyParts).first().toInt());
290 else if(!key.compare("Duration", Qt::CaseInsensitive))
292 if(!audioFile.fileDuration()) audioFile.setFileDuration(parseDuration(value));
298 unsigned int FileAnalyzer::parseYear(const QString &str)
300 if(str.startsWith("UTC", Qt::CaseInsensitive))
302 QDate date = QDate::fromString(str.mid(3).trimmed().left(10), "yyyy-MM-dd");
315 int year = str.toInt(&ok);
327 unsigned int FileAnalyzer::parseDuration(const QString &str)
331 time = QTime::fromString(str, "z'ms'");
334 return max(1, (time.hour() * 60 * 60) + (time.minute() * 60) + time.second());
337 time = QTime::fromString(str, "s's 'z'ms'");
340 return max(1, (time.hour() * 60 * 60) + (time.minute() * 60) + time.second());
343 time = QTime::fromString(str, "m'mn 's's'");
346 return max(1, (time.hour() * 60 * 60) + (time.minute() * 60) + time.second());
349 time = QTime::fromString(str, "h'h 'm'mn'");
352 return max(1, (time.hour() * 60 * 60) + (time.minute() * 60) + time.second());
358 unsigned int FileAnalyzer::filesAccepted(void)
360 return m_filesAccepted;
363 unsigned int FileAnalyzer::filesRejected(void)
365 return m_filesRejected - m_filesDenied;
368 unsigned int FileAnalyzer::filesDenied(void)
370 return m_filesDenied;
373 ////////////////////////////////////////////////////////////
375 ////////////////////////////////////////////////////////////