1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "PlaylistImporter.h"
25 #include <QStringList>
34 //Un-escape XML characters
40 g_xmlEscapeSequence
[] =
51 const char *PlaylistImporter::supportedExtensions
= "*.m3u *.m3u8 *.pls *.asx *.wpl";
54 ////////////////////////////////////////////////////////////
56 ////////////////////////////////////////////////////////////
58 bool PlaylistImporter::importPlaylist(QStringList
&fileList
, const QString
&playlistFile
)
60 QFileInfo
file(playlistFile
);
61 QDir
baseDir(file
.canonicalPath());
63 QDir
rootDir(baseDir
);
64 while(rootDir
.cdUp());
67 if(file
.size() < 3 || file
.size() > 512000)
72 //Detect playlist type
73 playlist_t playlistType
= isPlaylist(file
.canonicalFilePath());
75 //Exit if not a playlist
76 if(playlistType
== notPlaylist
)
81 QFile
data(playlistFile
);
83 //Open file for reading
84 if(!data
.open(QIODevice::ReadOnly
))
89 //Skip very large files (parsing could take very long)
90 if(data
.size() >= 10485760i64
)
92 qWarning("File is very big. Probably not a Playlist. Rejecting...");
96 //Parse playlist depending on type
100 return parsePlaylist_m3u(data
, fileList
, baseDir
, rootDir
);
103 return parsePlaylist_pls(data
, fileList
, baseDir
, rootDir
);
106 return parsePlaylist_wpl(data
, fileList
, baseDir
, rootDir
);
114 ////////////////////////////////////////////////////////////
116 ////////////////////////////////////////////////////////////
118 PlaylistImporter::playlist_t
PlaylistImporter::isPlaylist(const QString
&fileName
)
120 QFileInfo
file (fileName
);
122 if(file
.suffix().compare("m3u", Qt::CaseInsensitive
) == 0 || file
.suffix().compare("m3u8", Qt::CaseInsensitive
) == 0)
126 else if(file
.suffix().compare("pls", Qt::CaseInsensitive
) == 0)
130 else if(file
.suffix().compare("asx", Qt::CaseInsensitive
) == 0 || file
.suffix().compare("wpl", Qt::CaseInsensitive
) == 0)
140 bool PlaylistImporter::parsePlaylist_m3u(QFile
&data
, QStringList
&fileList
, const QDir
&baseDir
, const QDir
&rootDir
)
142 const QTextCodec
*codec
= QTextCodec::codecForName("System");
143 const bool preferUTF8
= data
.fileName().endsWith(".m3u8", Qt::CaseInsensitive
);
144 bool foundAtLeastOneFile
= false;
151 QByteArray line
= data
.readLine();
155 filePath
[0] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line
.constData(), line
.size()).trimmed()));
156 filePath
[1] = QString(QDir::fromNativeSeparators(codec
->toUnicode(line
.constData(), line
.size()).trimmed()));
157 filePath
[2] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line
.constData(), line
.size()).trimmed()));
161 filePath
[0] = QString(QDir::fromNativeSeparators(codec
->toUnicode(line
.constData(), line
.size()).trimmed()));
162 filePath
[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line
.constData(), line
.size()).trimmed()));
163 filePath
[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line
.constData(), line
.size()).trimmed()));
166 for(size_t i
= 0; i
< 3; i
++)
168 if(!(filePath
[i
].isEmpty() || filePath
[i
].startsWith("#") || filePath
[i
].contains(QChar(QChar::ReplacementCharacter
))))
170 QFileInfo
filename(filePath
[i
]);
171 filename
.setCaching(false);
172 fixFilePath(filename
, baseDir
, rootDir
);
174 if(filename
.exists() && filename
.isFile())
176 qDebug("Found: \"%s\"", filePath
[i
].toUtf8().constData());
177 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
179 fileList
<< filename
.canonicalFilePath();
181 foundAtLeastOneFile
= true;
188 //If we did not find any files yet, try UTF-16 now
189 if(!foundAtLeastOneFile
)
191 const char* codecs
[2] = {"UTF-16LE", "UTF-16BE"};
193 for(size_t i
= 0; i
< 2; i
++)
195 QTextStream
stream(&data
);
196 stream
.setAutoDetectUnicode(false);
197 stream
.setCodec(codecs
[i
]);
200 while(!stream
.atEnd())
202 QString filePath
= stream
.readLine().trimmed();
204 if(!(filePath
.isEmpty() || filePath
.startsWith("#") || filePath
.contains(QChar(QChar::ReplacementCharacter
))))
206 QFileInfo
filename(filePath
);
207 filename
.setCaching(false);
208 fixFilePath(filename
, baseDir
, rootDir
);
210 if(filename
.exists() && filename
.isFile())
212 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
214 fileList
<< filename
.canonicalFilePath();
216 foundAtLeastOneFile
= true;
221 if(foundAtLeastOneFile
) break;
228 bool PlaylistImporter::parsePlaylist_pls(QFile
&data
, QStringList
&fileList
, const QDir
&baseDir
, const QDir
&rootDir
)
230 QRegExp
plsEntry("File(\\d+)=(.+)", Qt::CaseInsensitive
);
231 const QTextCodec
*codec
= QTextCodec::codecForName("System");
232 bool foundAtLeastOneFile
= false;
239 QByteArray line
= data
.readLine();
241 filePath
[0] = QString(QDir::fromNativeSeparators(codec
->toUnicode(line
.constData(), line
.size()).trimmed()));
242 filePath
[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line
.constData(), line
.size()).trimmed()));
243 filePath
[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line
.constData(), line
.size()).trimmed()));
245 for(size_t i
= 0; i
< 3; i
++)
247 if(!filePath
[i
].contains(QChar(QChar::ReplacementCharacter
)))
249 if(plsEntry
.indexIn(filePath
[i
]) >= 0)
251 QFileInfo
filename(QDir::fromNativeSeparators(plsEntry
.cap(2)).trimmed());
252 filename
.setCaching(false);
253 fixFilePath(filename
, baseDir
, rootDir
);
255 if(filename
.exists() && filename
.isFile())
257 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
259 fileList
<< filename
.canonicalFilePath();
261 foundAtLeastOneFile
= true;
269 //If we did not find any files yet, try UTF-16 now
270 if(!foundAtLeastOneFile
)
272 const char* codecs
[2] = {"UTF-16LE", "UTF-16BE"};
274 for(size_t i
= 0; i
< 2; i
++)
276 QTextStream
stream(&data
);
277 stream
.setAutoDetectUnicode(false);
278 stream
.setCodec(codecs
[i
]);
281 while(!stream
.atEnd())
283 QString filePath
= stream
.readLine().trimmed();
285 if(!filePath
.contains(QChar(QChar::ReplacementCharacter
)))
287 if(plsEntry
.indexIn(filePath
) >= 0)
289 QFileInfo
filename(QDir::fromNativeSeparators(plsEntry
.cap(2)).trimmed());
290 filename
.setCaching(false);
291 fixFilePath(filename
, baseDir
, rootDir
);
293 if(filename
.exists() && filename
.isFile())
295 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
297 fileList
<< filename
.canonicalFilePath();
299 foundAtLeastOneFile
= true;
305 if(foundAtLeastOneFile
) break;
312 bool PlaylistImporter::parsePlaylist_wpl(QFile
&data
, QStringList
&fileList
, const QDir
&baseDir
, const QDir
&rootDir
)
314 QRegExp
skipData("<!--(.+)-->", Qt::CaseInsensitive
);
315 QRegExp
wplEntry("<(media|ref)[^<>]*(src|href)=\"([^\"]+)\"[^<>]*>", Qt::CaseInsensitive
);
317 skipData
.setMinimal(true);
319 QByteArray buffer
= data
.readAll();
320 QString line
= QString::fromUtf8(buffer
.constData(), buffer
.size()).simplified();
325 while((index
= skipData
.indexIn(line
)) >= 0)
327 line
.remove(index
, skipData
.matchedLength());
332 while((offset
= wplEntry
.indexIn(line
, offset
) + 1) > 0)
334 QFileInfo
filename(QDir::fromNativeSeparators(unescapeXml(wplEntry
.cap(3)).trimmed()));
335 filename
.setCaching(false);
336 fixFilePath(filename
, baseDir
, rootDir
);
338 if(filename
.exists() && filename
.isFile())
340 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
342 fileList
<< filename
.canonicalFilePath();
350 void PlaylistImporter::fixFilePath(QFileInfo
&filename
, const QDir
&baseDir
, const QDir
&rootDir
)
352 if(filename
.filePath().startsWith("/"))
354 while(filename
.filePath().startsWith("/"))
356 filename
.setFile(filename
.filePath().mid(1));
358 filename
.setFile(rootDir
.filePath(filename
.filePath()));
361 if(!filename
.isAbsolute())
363 filename
.setFile(baseDir
.filePath(filename
.filePath()));
367 QString
&PlaylistImporter::unescapeXml(QString
&str
)
369 for(int i
= 0; (g_xmlEscapeSequence
[i
].escape
&& g_xmlEscapeSequence
[i
].output
); i
++)
371 str
.replace(g_xmlEscapeSequence
[i
].escape
, g_xmlEscapeSequence
[i
].output
);