OSDN Git Service

Fixed detection of bit depth.
[lamexp/LameXP.git] / src / Thread_FileAnalyzer.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
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.
9 //
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.
14 //
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.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "Thread_FileAnalyzer.h"
23
24 #include "Global.h"
25 #include "LockedFile.h"
26 #include "Model_AudioFile.h"
27 #include "PlaylistImporter.h"
28
29 #include <QDir>
30 #include <QFileInfo>
31 #include <QProcess>
32 #include <QDate>
33 #include <QTime>
34 #include <QDebug>
35 #include <QImage>
36
37 #include <math.h>
38
39 #define IS_KEY(KEY) (key.compare(KEY, Qt::CaseInsensitive) == 0)
40 #define IS_SEC(SEC) (key.startsWith((SEC "_"), Qt::CaseInsensitive))
41 #define FIRST_TOK(STR) (STR.split(" ", QString::SkipEmptyParts).first())
42
43 ////////////////////////////////////////////////////////////
44 // Constructor
45 ////////////////////////////////////////////////////////////
46
47 FileAnalyzer::FileAnalyzer(const QStringList &inputFiles)
48 :
49         m_inputFiles(inputFiles),
50         m_mediaInfoBin(lamexp_lookup_tool("mediainfo.exe")),
51         m_avs2wavBin(lamexp_lookup_tool("avs2wav.exe")),
52         m_templateFile(NULL),
53         m_abortFlag(false)
54 {
55         m_bSuccess = false;
56         m_bAborted = false;
57                 
58         if(m_mediaInfoBin.isEmpty())
59         {
60                 qFatal("Invalid path to MediaInfo binary. Tool not initialized properly.");
61         }
62
63         m_filesAccepted = 0;
64         m_filesRejected = 0;
65         m_filesDenied = 0;
66         m_filesDummyCDDA = 0;
67         m_filesCueSheet = 0;
68 }
69
70 FileAnalyzer::~FileAnalyzer(void)
71 {
72         if(m_templateFile)
73         {
74                 QString templatePath = m_templateFile->filePath();
75                 LAMEXP_DELETE(m_templateFile);
76                 if(QFile::exists(templatePath)) QFile::remove(templatePath);
77         }
78 }
79
80 ////////////////////////////////////////////////////////////
81 // Static data
82 ////////////////////////////////////////////////////////////
83
84 const char *FileAnalyzer::g_tags_gen[] =
85 {
86         "ID",
87         "Format",
88         "Format_Profile",
89         "Format_Version",
90         "Duration",
91         "Title", "Track",
92         "Track/Position",
93         "Artist", "Performer",
94         "Album",
95         "Genre",
96         "Released_Date", "Recorded_Date",
97         "Comment",
98         "Cover",
99         "Cover_Type",
100         "Cover_Mime",
101         "Cover_Data",
102         NULL
103 };
104
105 const char *FileAnalyzer::g_tags_aud[] =
106 {
107         "ID",
108         "Format",
109         "Format_Profile",
110         "Format_Version",
111         "Channel(s)",
112         "SamplingRate",
113         "BitDepth",
114         "BitRate",
115         "BitRate_Mode",
116         NULL
117 };
118
119 ////////////////////////////////////////////////////////////
120 // Thread Main
121 ////////////////////////////////////////////////////////////
122
123 void FileAnalyzer::run()
124 {
125         m_bSuccess = false;
126         m_bAborted = false;
127
128         m_filesAccepted = 0;
129         m_filesRejected = 0;
130         m_filesDenied = 0;
131         m_filesDummyCDDA = 0;
132         m_filesCueSheet = 0;
133
134         m_inputFiles.sort();
135         m_recentlyAdded.clear();
136         m_abortFlag = false;
137
138         if(!m_templateFile)
139         {
140                 if(!createTemplate())
141                 {
142                         qWarning("Failed to create template file!");
143                         return;
144                 }
145         }
146
147         while(!m_inputFiles.isEmpty())
148         {
149                 int fileType = fileTypeNormal;
150                 QString currentFile = QDir::fromNativeSeparators(m_inputFiles.takeFirst());
151                 qDebug("Analyzing: %s", currentFile.toUtf8().constData());
152                 emit fileSelected(QFileInfo(currentFile).fileName());
153                 AudioFileModel file = analyzeFile(currentFile, &fileType);
154                 
155                 if(m_abortFlag)
156                 {
157                         MessageBeep(MB_ICONERROR);
158                         m_bAborted = true;
159                         qWarning("Operation cancelled by user!");
160                         return;
161                 }
162                 if(fileType == fileTypeSkip)
163                 {
164                         qWarning("File was recently added, skipping!");
165                         continue;
166                 }
167                 if(fileType == fileTypeDenied)
168                 {
169                         m_filesDenied++;
170                         qWarning("Cannot access file for reading, skipping!");
171                         continue;
172                 }
173                 if(fileType == fileTypeCDDA)
174                 {
175                         m_filesDummyCDDA++;
176                         qWarning("Dummy CDDA file detected, skipping!");
177                         continue;
178                 }
179                 
180                 if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty())
181                 {
182                         if(PlaylistImporter::importPlaylist(m_inputFiles, currentFile))
183                         {
184                                 qDebug("Imported playlist file.");
185                         }
186                         else if(!QFileInfo(currentFile).suffix().compare("cue", Qt::CaseInsensitive))
187                         {
188                                 qWarning("Cue Sheet file detected, skipping!");
189                                 m_filesCueSheet++;
190                         }
191                         else if(!QFileInfo(currentFile).suffix().compare("avs", Qt::CaseInsensitive))
192                         {
193                                 qDebug("Found a potential Avisynth script, investigating...");
194                                 if(analyzeAvisynthFile(currentFile, file))
195                                 {
196                                         m_filesAccepted++;
197                                         emit fileAnalyzed(file);
198                                 }
199                                 else
200                                 {
201                                         qDebug("Rejected Avisynth file: %s", file.filePath().toUtf8().constData());
202                                         m_filesRejected++;
203                                 }
204                         }
205                         else
206                         {
207                                 qDebug("Rejected file of unknown type: %s", file.filePath().toUtf8().constData());
208                                 m_filesRejected++;
209                         }
210                         continue;
211                 }
212
213                 m_filesAccepted++;
214                 m_recentlyAdded.append(file.filePath());
215                 emit fileAnalyzed(file);
216         }
217
218         qDebug("All files added.\n");
219         m_bSuccess = true;
220 }
221
222 ////////////////////////////////////////////////////////////
223 // Privtae Functions
224 ////////////////////////////////////////////////////////////
225
226 const AudioFileModel FileAnalyzer::analyzeFile(const QString &filePath, int *type)
227 {
228         *type = fileTypeNormal;
229         
230         AudioFileModel audioFile(filePath);
231
232         if(m_recentlyAdded.contains(filePath, Qt::CaseInsensitive))
233         {
234                 *type = fileTypeSkip;
235                 return audioFile;
236         }
237
238         QFile readTest(filePath);
239         if(!readTest.open(QIODevice::ReadOnly))
240         {
241                 *type = fileTypeDenied;
242                 return audioFile;
243         }
244         if(checkFile_CDDA(readTest))
245         {
246                 *type = fileTypeCDDA;
247                 return audioFile;
248         }
249         readTest.close();
250
251         bool skipNext = false;
252         unsigned int id_val[2] = {UINT_MAX, UINT_MAX};
253         cover_t coverType = coverNone;
254         QByteArray coverData;
255
256         QStringList params;
257         params << QString("--Inform=file://%1").arg(QDir::toNativeSeparators(m_templateFile->filePath()));
258         params << QDir::toNativeSeparators(filePath);
259         
260         QProcess process;
261         process.setProcessChannelMode(QProcess::MergedChannels);
262         process.setReadChannel(QProcess::StandardOutput);
263         process.start(m_mediaInfoBin, params);
264                 
265         if(!process.waitForStarted())
266         {
267                 qWarning("MediaInfo process failed to create!");
268                 qWarning("Error message: \"%s\"\n", process.errorString().toLatin1().constData());
269                 process.kill();
270                 process.waitForFinished(-1);
271                 return audioFile;
272         }
273
274         while(process.state() != QProcess::NotRunning)
275         {
276                 if(m_abortFlag)
277                 {
278                         process.kill();
279                         qWarning("Process was aborted on user request!");
280                         break;
281                 }
282                 
283                 if(!process.waitForReadyRead())
284                 {
285                         if(process.state() == QProcess::Running)
286                         {
287                                 qWarning("MediaInfo time out. Killing process and skipping file!");
288                                 process.kill();
289                                 process.waitForFinished(-1);
290                                 return audioFile;
291                         }
292                 }
293
294                 QByteArray data;
295
296                 while(process.canReadLine())
297                 {
298                         QString line = QString::fromUtf8(process.readLine().constData()).simplified();
299                         if(!line.isEmpty())
300                         {
301                                 //qDebug("Line:%s", line.toUtf8().constData());
302                                 
303                                 int index = line.indexOf('=');
304                                 if(index > 0)
305                                 {
306                                         QString key = line.left(index).trimmed();
307                                         QString val = line.mid(index+1).trimmed();
308                                         if(!key.isEmpty())
309                                         {
310                                                 updateInfo(audioFile, &skipNext, id_val, &coverType, &coverData, key, val);
311                                         }
312                                 }
313                         }
314                 }
315         }
316
317         if(audioFile.fileName().isEmpty())
318         {
319                 QString baseName = QFileInfo(filePath).fileName();
320                 int index = baseName.lastIndexOf(".");
321
322                 if(index >= 0)
323                 {
324                         baseName = baseName.left(index);
325                 }
326
327                 baseName = baseName.replace("_", " ").simplified();
328                 index = baseName.lastIndexOf(" - ");
329
330                 if(index >= 0)
331                 {
332                         baseName = baseName.mid(index + 3).trimmed();
333                 }
334
335                 audioFile.setFileName(baseName);
336         }
337         
338         process.waitForFinished();
339         if(process.state() != QProcess::NotRunning)
340         {
341                 process.kill();
342                 process.waitForFinished(-1);
343         }
344
345         if((coverType != coverNone) && (!coverData.isEmpty()))
346         {
347                 retrieveCover(audioFile, coverType, coverData);
348         }
349
350         return audioFile;
351 }
352
353 void FileAnalyzer::updateInfo(AudioFileModel &audioFile, bool *skipNext, unsigned int *id_val, cover_t *coverType, QByteArray *coverData, const QString &key, const QString &value)
354 {
355         //qWarning("'%s' -> '%s'", key.toUtf8().constData(), value.toUtf8().constData());
356         
357         /*New Stream*/
358         if(IS_KEY("Gen_ID") || IS_KEY("Aud_ID"))
359         {
360                 if(value.isEmpty())
361                 {
362                         *skipNext = false;
363                 }
364                 else
365                 {
366                         //We ignore all ID's, except for the lowest one!
367                         bool ok = false;
368                         unsigned int id = value.toUInt(&ok);
369                         if(ok)
370                         {
371                                 if(IS_KEY("Gen_ID")) { id_val[0] = qMin(id_val[0], id); *skipNext = (id > id_val[0]); }
372                                 if(IS_KEY("Aud_ID")) { id_val[1] = qMin(id_val[1], id); *skipNext = (id > id_val[1]); }
373                         }
374                         else
375                         {
376                                 *skipNext = true;
377                         }
378                 }
379                 if(*skipNext)
380                 {
381                         qWarning("Skipping info for non-primary stream!");
382                 }
383                 return;
384         }
385
386         /*Skip?*/
387         if((*skipNext) || value.isEmpty())
388         {
389                 return;
390         }
391
392         /*General Section*/
393         if(IS_SEC("Gen"))
394         {
395                 if(IS_KEY("Gen_Format"))
396                 {
397                         audioFile.setFormatContainerType(value);
398                 }
399                 else if(IS_KEY("Gen_Format_Profile"))
400                 {
401                         audioFile.setFormatContainerProfile(value);
402                 }
403                 else if(IS_KEY("Gen_Title") || IS_KEY("Gen_Track"))
404                 {
405                         audioFile.setFileName(value);
406                 }
407                 else if(IS_KEY("Gen_Duration"))
408                 {
409                         unsigned int tmp = parseDuration(value);
410                         if(tmp > 0) audioFile.setFileDuration(tmp);
411                 }
412                 else if(IS_KEY("Gen_Artist") || IS_KEY("Gen_Performer"))
413                 {
414                         audioFile.setFileArtist(value);
415                 }
416                 else if(IS_KEY("Gen_Album"))
417                 {
418                         audioFile.setFileAlbum(value);
419                 }
420                 else if(IS_KEY("Gen_Genre"))
421                 {
422                         audioFile.setFileGenre(value);
423                 }
424                 else if(IS_KEY("Gen_Released_Date") || IS_KEY("Gen_Recorded_Date"))
425                 {
426                         unsigned int tmp = parseYear(value);
427                         if(tmp > 0) audioFile.setFileYear(tmp);
428                 }
429                 else if(IS_KEY("Gen_Comment"))
430                 {
431                         audioFile.setFileComment(value);
432                 }
433                 else if(IS_KEY("Gen_Track/Position"))
434                 {
435                         bool ok = false;
436                         unsigned int tmp = value.toUInt(&ok);
437                         if(ok) audioFile.setFilePosition(tmp);
438                 }
439                 else if(IS_KEY("Gen_Cover") || IS_KEY("Gen_Cover_Type"))
440                 {
441                         if(*coverType == coverNone)
442                         {
443                                 *coverType = coverJpeg;
444                         }
445                 }
446                 else if(IS_KEY("Gen_Cover_Mime"))
447                 {
448                         QString temp = FIRST_TOK(value);
449                         if(!temp.compare("image/jpeg", Qt::CaseInsensitive)) *coverType = coverJpeg;
450                         else if(!temp.compare("image/png", Qt::CaseInsensitive)) *coverType = coverPng;
451                         else if(!temp.compare("image/gif", Qt::CaseInsensitive)) *coverType = coverGif;
452                 }
453                 else if(IS_KEY("Gen_Cover_Data"))
454                 {
455                         if(!coverData->isEmpty()) coverData->clear();
456                         coverData->append(QByteArray::fromBase64(FIRST_TOK(value).toLatin1()));
457                 }
458                 else
459                 {
460                         qWarning("Unknown key '%s' with value '%s' found!", key.toUtf8().constData(), value.toUtf8().constData());
461                 }
462                 return;
463         }
464
465         /*Audio Section*/
466         if(IS_SEC("Aud"))
467         {
468
469                 if(IS_KEY("Aud_Format"))
470                 {
471                         audioFile.setFormatAudioType(value);
472                 }
473                 else if(IS_KEY("Aud_Format_Profile"))
474                 {
475                         audioFile.setFormatAudioProfile(value);
476                 }
477                 else if(IS_KEY("Aud_Format_Version"))
478                 {
479                         audioFile.setFormatAudioVersion(value);
480                 }
481                 else if(IS_KEY("Aud_Channel(s)"))
482                 {
483                         bool ok = false;
484                         unsigned int tmp = value.toUInt(&ok);
485                         if(ok) audioFile.setFormatAudioChannels(tmp);
486                 }
487                 else if(IS_KEY("Aud_SamplingRate"))
488                 {
489                         bool ok = false;
490                         unsigned int tmp = value.toUInt(&ok);
491                         if(ok) audioFile.setFormatAudioSamplerate(tmp);
492                 }
493                 else if(IS_KEY("Aud_BitDepth"))
494                 {
495                         bool ok = false;
496                         unsigned int tmp = value.toUInt(&ok);
497                         if(ok) audioFile.setFormatAudioBitdepth(tmp);
498                 }
499                 else if(IS_KEY("Aud_Duration"))
500                 {
501                         unsigned int tmp = parseDuration(value);
502                         if(tmp > 0) audioFile.setFileDuration(tmp);
503                 }
504                 else if(IS_KEY("Aud_BitRate"))
505                 {
506                         bool ok = false;
507                         unsigned int tmp = value.toUInt(&ok);
508                         if(ok) audioFile.setFormatAudioBitrate(tmp/1000);
509                 }
510                 else if(IS_KEY("Aud_BitRate_Mode"))
511                 {
512                         if(!value.compare("CBR", Qt::CaseInsensitive)) audioFile.setFormatAudioBitrateMode(AudioFileModel::BitrateModeConstant);
513                         if(!value.compare("VBR", Qt::CaseInsensitive)) audioFile.setFormatAudioBitrateMode(AudioFileModel::BitrateModeVariable);
514                 }
515                 else
516                 {
517                         qWarning("Unknown key '%s' with value '%s' found!", key.toUtf8().constData(), value.toUtf8().constData());
518                 }
519                 return;
520         }
521
522         /*Section not recognized*/
523         qWarning("Unknown section: %s", key.toUtf8().constData());
524 }
525
526 bool FileAnalyzer::checkFile_CDDA(QFile &file)
527 {
528         file.reset();
529         QByteArray data = file.read(128);
530         
531         int i = data.indexOf("RIFF");
532         int j = data.indexOf("CDDA");
533         int k = data.indexOf("fmt ");
534
535         return ((i >= 0) && (j >= 0) && (k >= 0) && (k > j) && (j > i));
536 }
537
538 void FileAnalyzer::retrieveCover(AudioFileModel &audioFile, cover_t coverType, const QByteArray &coverData)
539 {
540         qDebug("Retrieving cover!");
541         QString extension;
542
543         switch(coverType)
544         {
545         case coverPng:
546                 extension = QString::fromLatin1("png");
547                 break;
548         case coverGif:
549                 extension = QString::fromLatin1("gif");
550                 break;
551         default:
552                 extension = QString::fromLatin1("jpg");
553                 break;
554         }
555         
556         if(!(QImage::fromData(coverData, extension.toUpper().toLatin1().constData()).isNull()))
557         {
558                 QFile coverFile(QString("%1/%2.%3").arg(lamexp_temp_folder2(), lamexp_rand_str(), extension));
559                 if(coverFile.open(QIODevice::WriteOnly))
560                 {
561                         coverFile.write(coverData);
562                         coverFile.close();
563                         audioFile.setFileCover(coverFile.fileName(), true);
564                 }
565         }
566         else
567         {
568                 qWarning("Image data seems to be invalid :-(");
569         }
570 }
571
572 bool FileAnalyzer::analyzeAvisynthFile(const QString &filePath, AudioFileModel &info)
573 {
574         QProcess process;
575         process.setProcessChannelMode(QProcess::MergedChannels);
576         process.setReadChannel(QProcess::StandardOutput);
577         process.start(m_avs2wavBin, QStringList() << QDir::toNativeSeparators(filePath) << "?");
578
579         if(!process.waitForStarted())
580         {
581                 qWarning("AVS2WAV process failed to create!");
582                 qWarning("Error message: \"%s\"\n", process.errorString().toLatin1().constData());
583                 process.kill();
584                 process.waitForFinished(-1);
585                 return false;
586         }
587
588         bool bInfoHeaderFound = false;
589
590         while(process.state() != QProcess::NotRunning)
591         {
592                 if(m_abortFlag)
593                 {
594                         process.kill();
595                         qWarning("Process was aborted on user request!");
596                         break;
597                 }
598                 
599                 if(!process.waitForReadyRead())
600                 {
601                         if(process.state() == QProcess::Running)
602                         {
603                                 qWarning("AVS2WAV time out. Killing process and skipping file!");
604                                 process.kill();
605                                 process.waitForFinished(-1);
606                                 return false;
607                         }
608                 }
609
610                 QByteArray data;
611
612                 while(process.canReadLine())
613                 {
614                         QString line = QString::fromUtf8(process.readLine().constData()).simplified();
615                         if(!line.isEmpty())
616                         {
617                                 int index = line.indexOf(':');
618                                 if(index > 0)
619                                 {
620                                         QString key = line.left(index).trimmed();
621                                         QString val = line.mid(index+1).trimmed();
622
623                                         if(bInfoHeaderFound && !key.isEmpty() && !val.isEmpty())
624                                         {
625                                                 if(key.compare("TotalSeconds", Qt::CaseInsensitive) == 0)
626                                                 {
627                                                         bool ok = false;
628                                                         unsigned int duration = val.toUInt(&ok);
629                                                         if(ok) info.setFileDuration(duration);
630                                                 }
631                                                 if(key.compare("SamplesPerSec", Qt::CaseInsensitive) == 0)
632                                                 {
633                                                         bool ok = false;
634                                                         unsigned int samplerate = val.toUInt(&ok);
635                                                         if(ok) info.setFormatAudioSamplerate (samplerate);
636                                                 }
637                                                 if(key.compare("Channels", Qt::CaseInsensitive) == 0)
638                                                 {
639                                                         bool ok = false;
640                                                         unsigned int channels = val.toUInt(&ok);
641                                                         if(ok) info.setFormatAudioChannels(channels);
642                                                 }
643                                                 if(key.compare("BitsPerSample", Qt::CaseInsensitive) == 0)
644                                                 {
645                                                         bool ok = false;
646                                                         unsigned int bitdepth = val.toUInt(&ok);
647                                                         if(ok) info.setFormatAudioBitdepth(bitdepth);
648                                                 }                                       
649                                         }
650                                 }
651                                 else
652                                 {
653                                         if(line.contains("[Audio Info]", Qt::CaseInsensitive))
654                                         {
655                                                 info.setFormatAudioType("Avisynth");
656                                                 info.setFormatContainerType("Avisynth");
657                                                 bInfoHeaderFound = true;
658                                         }
659                                 }
660                         }
661                 }
662         }
663         
664         process.waitForFinished();
665         if(process.state() != QProcess::NotRunning)
666         {
667                 process.kill();
668                 process.waitForFinished(-1);
669         }
670
671         //Check exit code
672         switch(process.exitCode())
673         {
674         case 0:
675                 qDebug("Avisynth script was analyzed successfully.");
676                 return true;
677                 break;
678         case -5:
679                 qWarning("It appears that Avisynth is not installed on the system!");
680                 return false;
681                 break;
682         default:
683                 qWarning("Failed to open the Avisynth script, bad AVS file?");
684                 return false;
685                 break;
686         }
687 }
688
689 bool FileAnalyzer::createTemplate(void)
690 {
691         if(m_templateFile)
692         {
693                 qWarning("Template file already exists!");
694                 return true;
695         }
696         
697         QString templatePath = QString("%1/%2.txt").arg(lamexp_temp_folder2(), lamexp_rand_str());
698
699         QFile templateFile(templatePath);
700         if(!templateFile.open(QIODevice::WriteOnly))
701         {
702                 return false;
703         }
704
705         templateFile.write("General;");
706         for(size_t i = 0; g_tags_gen[i]; i++)
707         {
708                 templateFile.write(QString("Gen_%1=%%1%\\n").arg(g_tags_gen[i]).toLatin1().constData());
709         }
710         templateFile.write("\\n\r\n");
711
712         templateFile.write("Audio;");
713         for(size_t i = 0; g_tags_aud[i]; i++)
714         {
715                 templateFile.write(QString("Aud_%1=%%1%\\n").arg(g_tags_aud[i]).toLatin1().constData());
716         }
717         templateFile.write("\\n\r\n");
718
719         bool success = (templateFile.error() == QFile::NoError);
720         templateFile.close();
721         
722         if(!success)
723         {
724                 QFile::remove(templatePath);
725                 return false;
726         }
727
728         try
729         {
730                 m_templateFile = new LockedFile(templatePath);
731         }
732         catch(...)
733         {
734                 qWarning("Failed to lock template file!");
735                 return false;
736         }
737
738         return true;
739 }
740
741 unsigned int FileAnalyzer::parseYear(const QString &str)
742 {
743         if(str.startsWith("UTC", Qt::CaseInsensitive))
744         {
745                 QDate date = QDate::fromString(str.mid(3).trimmed().left(10), "yyyy-MM-dd");
746                 if(date.isValid())
747                 {
748                         return date.year();
749                 }
750                 else
751                 {
752                         return 0;
753                 }
754         }
755         else
756         {
757                 bool ok = false;
758                 int year = str.toInt(&ok);
759                 if(ok && year > 0)
760                 {
761                         return year;
762                 }
763                 else
764                 {
765                         return 0;
766                 }
767         }
768 }
769
770 unsigned int FileAnalyzer::parseDuration(const QString &str)
771 {
772         bool ok = false;
773         unsigned int value = str.toUInt(&ok);
774         return ok ? (value/1000) : 0;
775 }
776
777 ////////////////////////////////////////////////////////////
778 // Public Functions
779 ////////////////////////////////////////////////////////////
780
781 unsigned int FileAnalyzer::filesAccepted(void)
782 {
783         return m_filesAccepted;
784 }
785
786 unsigned int FileAnalyzer::filesRejected(void)
787 {
788         return m_filesRejected;
789 }
790
791 unsigned int FileAnalyzer::filesDenied(void)
792 {
793         return m_filesDenied;
794 }
795
796 unsigned int FileAnalyzer::filesDummyCDDA(void)
797 {
798         return m_filesDummyCDDA;
799 }
800
801 unsigned int FileAnalyzer::filesCueSheet(void)
802 {
803         return m_filesCueSheet;
804 }
805
806 ////////////////////////////////////////////////////////////
807 // EVENTS
808 ////////////////////////////////////////////////////////////
809
810 /*NONE*/