OSDN Git Service

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