diff --git a/LameXP.vcproj b/LameXP.vcproj index 9baffc63..5dd430cf 100644 --- a/LameXP.vcproj +++ b/LameXP.vcproj @@ -470,6 +470,10 @@ RelativePath=".\src\Model_Settings.cpp" > + + @@ -1322,6 +1326,10 @@ RelativePath=".\src\Model_Settings.h" > + + diff --git a/src/PlaylistImporter.cpp b/src/PlaylistImporter.cpp new file mode 100644 index 00000000..96b3bc7e --- /dev/null +++ b/src/PlaylistImporter.cpp @@ -0,0 +1,281 @@ +/////////////////////////////////////////////////////////////////////////////// +// LameXP - Audio Encoder Front-End +// Copyright (C) 2004-2011 LoRd_MuldeR +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// http://www.gnu.org/licenses/gpl-2.0.txt +/////////////////////////////////////////////////////////////////////////////// + +#include "PlaylistImporter.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +//Un-escape XML characters +static const struct +{ + char *escape; + char *output; +} +g_xmlEscapeSequence[] = +{ + {"&", "&"}, + {"<", "<"}, + {">", ">"}, + {"'", "'"}, + {" ", " "}, + {""", "\""}, + {NULL, NULL} +}; + +//////////////////////////////////////////////////////////// +// Public Functions +//////////////////////////////////////////////////////////// + +bool PlaylistImporter::importPlaylist(QStringList &fileList, const QString &playlistFile) +{ + QFileInfo file(playlistFile); + QDir baseDir(file.canonicalPath()); + + QDir rootDir(baseDir); + while(rootDir.cdUp()); + + //Sanity check + if(file.size() < 3 || file.size() > 512000) + { + return false; + } + + //Detect playlist type + playlist_t playlistType = isPlaylist(file.canonicalFilePath()); + + //Exit if not a playlist + if(playlistType == notPlaylist) + { + return false; + } + + QFile data(playlistFile); + + //Open file for reading + if(!data.open(QIODevice::ReadOnly)) + { + return false; + } + + //Parse playlist depending on type + switch(playlistType) + { + case m3uPlaylist: + return parsePlaylist_m3u(data, fileList, baseDir, rootDir); + break; + case plsPlaylist: + return parsePlaylist_pls(data, fileList, baseDir, rootDir); + break; + case wplPlaylist: + return parsePlaylist_wpl(data, fileList, baseDir, rootDir); + break; + default: + return false; + break; + } +} + +//////////////////////////////////////////////////////////// +// Private Functions +//////////////////////////////////////////////////////////// + +PlaylistImporter::playlist_t PlaylistImporter::isPlaylist(const QString &fileName) +{ + QFileInfo file (fileName); + + if(file.suffix().compare("m3u", Qt::CaseInsensitive) == 0 || file.suffix().compare("m3u8", Qt::CaseInsensitive) == 0) + { + return m3uPlaylist; + } + else if(file.suffix().compare("pls", Qt::CaseInsensitive) == 0) + { + return plsPlaylist; + } + else if(file.suffix().compare("asx", Qt::CaseInsensitive) == 0 || file.suffix().compare("wpl", Qt::CaseInsensitive) == 0) + { + return wplPlaylist; + } + else + { + return notPlaylist; + } +} + +bool PlaylistImporter::parsePlaylist_m3u(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir) +{ + QByteArray line = data.readLine(); + + while(line.size() > 0) + { + QFileInfo filename1(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed())); + QFileInfo filename2(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed())); + + filename1.setCaching(false); + filename2.setCaching(false); + + if(!(filename1.filePath().startsWith("#") || filename2.filePath().startsWith("#"))) + { + fixFilePath(filename1, baseDir, rootDir); + fixFilePath(filename2, baseDir, rootDir); + + if(filename1.exists()) + { + if(isPlaylist(filename1.canonicalFilePath()) == notPlaylist) + { + fileList << filename1.canonicalFilePath(); + } + } + else if(filename2.exists()) + { + if(isPlaylist(filename2.canonicalFilePath()) == notPlaylist) + { + fileList << filename2.canonicalFilePath(); + } + } + } + + line = data.readLine(); + } + + return true; +} + +bool PlaylistImporter::parsePlaylist_pls(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir) +{ + QRegExp plsEntry("File(\\d+)=(.+)", Qt::CaseInsensitive); + QByteArray line = data.readLine(); + + while(line.size() > 0) + { + bool flag = false; + + QString temp1(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed())); + QString temp2(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed())); + + if(!flag && plsEntry.indexIn(temp1) >= 0) + { + QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed()); + filename.setCaching(false); + fixFilePath(filename, baseDir, rootDir); + + if(filename.exists()) + { + if(isPlaylist(filename.canonicalFilePath()) == notPlaylist) + { + fileList << filename.canonicalFilePath(); + } + flag = true; + } + } + + if(!flag && plsEntry.indexIn(temp2) >= 0) + { + QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed()); + filename.setCaching(false); + fixFilePath(filename, baseDir, rootDir); + + if(filename.exists()) + { + if(isPlaylist(filename.canonicalFilePath()) == notPlaylist) + { + fileList << filename.canonicalFilePath(); + } + flag = true; + } + } + + line = data.readLine(); + } + + return true; +} + +bool PlaylistImporter::parsePlaylist_wpl(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir) +{ + QRegExp skipData("", Qt::CaseInsensitive); + QRegExp wplEntry("<(media|ref)[^<>]*(src|href)=\"([^\"]+)\"[^<>]*>", Qt::CaseInsensitive); + + skipData.setMinimal(true); + + QByteArray buffer = data.readAll(); + QString line = QString::fromUtf8(buffer.constData(), buffer.size()).simplified(); + buffer.clear(); + + int index = 0; + + while((index = skipData.indexIn(line)) >= 0) + { + line.remove(index, skipData.matchedLength()); + } + + int offset = 0; + + while((offset = wplEntry.indexIn(line, offset) + 1) > 0) + { + QFileInfo filename(QDir::fromNativeSeparators(unescapeXml(wplEntry.cap(3)).trimmed())); + filename.setCaching(false); + fixFilePath(filename, baseDir, rootDir); + + if(filename.exists()) + { + if(isPlaylist(filename.canonicalFilePath()) == notPlaylist) + { + fileList << filename.canonicalFilePath(); + } + } + } + + return true; +} + +void PlaylistImporter::fixFilePath(QFileInfo &filename, const QDir &baseDir, const QDir &rootDir) +{ + if(filename.filePath().startsWith("/")) + { + while(filename.filePath().startsWith("/")) + { + filename.setFile(filename.filePath().mid(1)); + } + filename.setFile(rootDir.filePath(filename.filePath())); + } + + if(!filename.isAbsolute()) + { + filename.setFile(baseDir.filePath(filename.filePath())); + } +} + +QString &PlaylistImporter::unescapeXml(QString &str) +{ + for(int i = 0; (g_xmlEscapeSequence[i].escape && g_xmlEscapeSequence[i].output); i++) + { + str.replace(g_xmlEscapeSequence[i].escape, g_xmlEscapeSequence[i].output); + } + + return str; +} diff --git a/src/PlaylistImporter.h b/src/PlaylistImporter.h new file mode 100644 index 00000000..60fbb5a8 --- /dev/null +++ b/src/PlaylistImporter.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////// +// LameXP - Audio Encoder Front-End +// Copyright (C) 2004-2011 LoRd_MuldeR +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// http://www.gnu.org/licenses/gpl-2.0.txt +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +class QFile; +class QDir; +class QFileInfo; +class QStringList; +class QString; + +class PlaylistImporter +{ +public: + enum playlist_t + { + notPlaylist, + m3uPlaylist, + plsPlaylist, + wplPlaylist + }; + + static bool importPlaylist(QStringList &fileList, const QString &playlistFile); + +private: + PlaylistImporter(void) {} + ~PlaylistImporter(void) {} + + static bool parsePlaylist_m3u(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir); + static bool parsePlaylist_pls(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir); + static bool parsePlaylist_wpl(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir); + + static playlist_t isPlaylist(const QString &fileName); + static void fixFilePath(QFileInfo &filename, const QDir &baseDir, const QDir &rootDir); + static QString &PlaylistImporter::unescapeXml(QString &str); +}; diff --git a/src/Thread_FileAnalyzer.cpp b/src/Thread_FileAnalyzer.cpp index a3c27567..5b71db3f 100644 --- a/src/Thread_FileAnalyzer.cpp +++ b/src/Thread_FileAnalyzer.cpp @@ -24,6 +24,7 @@ #include "Global.h" #include "LockedFile.h" #include "Model_AudioFile.h" +#include "PlaylistImporter.h" #include #include @@ -31,13 +32,9 @@ #include #include #include -#include #include -//Un-escape XML characters -#define XML_DECODE replace("&", "&").replace("'", "'").replace(" ", " ").replace(""", "\"").replace("<", "<").replace(">", ">") - //////////////////////////////////////////////////////////// // Constructor //////////////////////////////////////////////////////////// @@ -82,7 +79,7 @@ void FileAnalyzer::run() AudioFileModel file = analyzeFile(currentFile); if(file.fileName().isEmpty() || file.formatContainerType().isEmpty() || file.formatAudioType().isEmpty()) { - if(!importPlaylist(m_inputFiles, currentFile)) + if(!PlaylistImporter::importPlaylist(m_inputFiles, currentFile)) { m_filesRejected++; qDebug("Skipped: %s", file.filePath().toUtf8().constData()); @@ -362,230 +359,6 @@ unsigned int FileAnalyzer::parseDuration(const QString &str) return 0; } - -bool FileAnalyzer::importPlaylist(QStringList &fileList, const QString &playlistFile) -{ - QFileInfo file(playlistFile); - QDir baseDir(file.canonicalPath()); - - QDir rootDir(baseDir); - while(rootDir.cdUp()); - - //Sanity check - if(file.size() < 3 || file.size() > 512000) - { - return false; - } - - //Detect playlist type - playlist_t playlistType = isPlaylist(file.canonicalFilePath()); - - //Exit if not a playlist - if(playlistType == noPlaylist) - { - return false; - } - - QFile data(playlistFile); - - //Open file for reading - if(!data.open(QIODevice::ReadOnly)) - { - return false; - } - - //Parse playlist depending on type - switch(playlistType) - { - case m3uPlaylist: - return parsePlaylist_m3u(data, fileList, baseDir, rootDir); - break; - case plsPlaylist: - return parsePlaylist_pls(data, fileList, baseDir, rootDir); - break; - case wplPlaylist: - return parsePlaylist_wpl(data, fileList, baseDir, rootDir); - break; - default: - return false; - break; - } -} - -bool FileAnalyzer::parsePlaylist_m3u(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir) -{ - QByteArray line = data.readLine(); - - while(line.size() > 0) - { - QFileInfo filename1(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed())); - QFileInfo filename2(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed())); - - filename1.setCaching(false); - filename2.setCaching(false); - - if(!(filename1.filePath().startsWith("#") || filename2.filePath().startsWith("#"))) - { - fixFilePath(filename1, baseDir, rootDir); - fixFilePath(filename2, baseDir, rootDir); - - if(filename1.exists()) - { - if(isPlaylist(filename1.canonicalFilePath()) == noPlaylist) - { - fileList << filename1.canonicalFilePath(); - } - } - else if(filename2.exists()) - { - if(isPlaylist(filename2.canonicalFilePath()) == noPlaylist) - { - fileList << filename2.canonicalFilePath(); - } - } - } - - line = data.readLine(); - } - - return true; -} - -bool FileAnalyzer::parsePlaylist_pls(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir) -{ - QRegExp plsEntry("File(\\d+)=(.+)", Qt::CaseInsensitive); - QByteArray line = data.readLine(); - - while(line.size() > 0) - { - bool flag = false; - - QString temp1(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed())); - QString temp2(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed())); - - if(!flag && plsEntry.indexIn(temp1) >= 0) - { - QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed()); - filename.setCaching(false); - fixFilePath(filename, baseDir, rootDir); - - if(filename.exists()) - { - if(isPlaylist(filename.canonicalFilePath()) == noPlaylist) - { - fileList << filename.canonicalFilePath(); - flag = true; - } - } - } - - if(!flag && plsEntry.indexIn(temp2) >= 0) - { - QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed()); - filename.setCaching(false); - fixFilePath(filename, baseDir, rootDir); - - if(filename.exists()) - { - if(isPlaylist(filename.canonicalFilePath()) == noPlaylist) - { - fileList << filename.canonicalFilePath(); - flag = true; - } - } - } - - line = data.readLine(); - } - - return true; -} - -bool FileAnalyzer::parsePlaylist_wpl(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir) -{ - QRegExp skipData("", Qt::CaseInsensitive); - QRegExp wplEntry("<(media|ref)[^<>]*(src|href)=\"([^\"]+)\"[^<>]*>", Qt::CaseInsensitive); - - skipData.setMinimal(true); - - QByteArray buffer = data.readAll(); - QString line = QString::fromUtf8(buffer.constData(), buffer.size()).simplified(); - buffer.clear(); - - int index = 0; - - while((index = skipData.indexIn(line)) >= 0) - { - line.remove(index, skipData.matchedLength()); - } - - int offset = 0; - - while((offset = wplEntry.indexIn(line, offset) + 1) > 0) - { - QFileInfo filename(QDir::fromNativeSeparators(wplEntry.cap(3).XML_DECODE.trimmed())); - filename.setCaching(false); - fixFilePath(filename, baseDir, rootDir); - - if(filename.exists()) - { - if(isPlaylist(filename.canonicalFilePath()) == noPlaylist) - { - fileList << filename.canonicalFilePath(); - } - } - } - - return true; -} - -FileAnalyzer::playlist_t FileAnalyzer::isPlaylist(const QString &fileName) -{ - QFileInfo file (fileName); - - if(file.suffix().compare("m3u", Qt::CaseInsensitive) == 0) - { - return m3uPlaylist; - } - else if(file.suffix().compare("m3u8", Qt::CaseInsensitive) == 0) - { - return m3uPlaylist; - } - else if(file.suffix().compare("pls", Qt::CaseInsensitive) == 0) - { - return plsPlaylist; - } - else if(file.suffix().compare("asx", Qt::CaseInsensitive) == 0) - { - return wplPlaylist; - } - else if(file.suffix().compare("wpl", Qt::CaseInsensitive) == 0) - { - return wplPlaylist; - } - else - { - return noPlaylist; - } -} - -void FileAnalyzer::fixFilePath(QFileInfo &filename, const QDir &baseDir, const QDir &rootDir) -{ - if(filename.filePath().startsWith("/")) - { - while(filename.filePath().startsWith("/")) - { - filename.setFile(filename.filePath().mid(1)); - } - filename.setFile(rootDir.filePath(filename.filePath())); - } - - if(!filename.isAbsolute()) - { - filename.setFile(baseDir.filePath(filename.filePath())); - } -} - //////////////////////////////////////////////////////////// // Public Functions //////////////////////////////////////////////////////////// diff --git a/src/Thread_FileAnalyzer.h b/src/Thread_FileAnalyzer.h index d3d2bf9c..08f57653 100644 --- a/src/Thread_FileAnalyzer.h +++ b/src/Thread_FileAnalyzer.h @@ -58,27 +58,12 @@ private: sectionAudio, sectionOther }; - - enum playlist_t - { - noPlaylist, - m3uPlaylist, - plsPlaylist, - wplPlaylist - }; const AudioFileModel analyzeFile(const QString &filePath); void updateInfo(AudioFileModel &audioFile, const QString &key, const QString &value); void updateSection(const QString §ion); unsigned int parseYear(const QString &str); unsigned int parseDuration(const QString &str); - bool importPlaylist(QStringList &fileList, const QString &playlistFile); - bool parsePlaylist_m3u(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir); - bool parsePlaylist_pls(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir); - bool parsePlaylist_wpl(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir); - playlist_t isPlaylist(const QString &fileName); - void fixFilePath(QFileInfo &filename, const QDir &baseDir, const QDir &rootDir); - QStringList m_inputFiles; const QString m_mediaInfoBin_x86;