OSDN Git Service

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