1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2013 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());
66 //Detect playlist type
67 playlist_t playlistType
= isPlaylist(file
.canonicalFilePath());
69 //Exit if not a playlist
70 if(playlistType
== notPlaylist
)
75 QFile
data(playlistFile
);
77 //Open file for reading
78 if(!data
.open(QIODevice::ReadOnly
))
83 //Skip very large files (parsing could take very long)
84 if((file
.size() < 3) || (file
.size() > 524288))
86 qWarning("File is very big. Probably not a Playlist. Rejecting...");
90 //Parse playlist depending on type
94 return parsePlaylist_m3u(data
, fileList
, baseDir
, rootDir
);
97 return parsePlaylist_pls(data
, fileList
, baseDir
, rootDir
);
100 return parsePlaylist_wpl(data
, fileList
, baseDir
, rootDir
);
108 ////////////////////////////////////////////////////////////
110 ////////////////////////////////////////////////////////////
112 PlaylistImporter::playlist_t
PlaylistImporter::isPlaylist(const QString
&fileName
)
114 QFileInfo
file (fileName
);
116 if(file
.suffix().compare("m3u", Qt::CaseInsensitive
) == 0 || file
.suffix().compare("m3u8", Qt::CaseInsensitive
) == 0)
120 else if(file
.suffix().compare("pls", Qt::CaseInsensitive
) == 0)
124 else if(file
.suffix().compare("asx", Qt::CaseInsensitive
) == 0 || file
.suffix().compare("wpl", Qt::CaseInsensitive
) == 0)
134 bool PlaylistImporter::parsePlaylist_m3u(QFile
&data
, QStringList
&fileList
, const QDir
&baseDir
, const QDir
&rootDir
)
136 const QTextCodec
*codec
= QTextCodec::codecForName("System");
137 const bool preferUTF8
= data
.fileName().endsWith(".m3u8", Qt::CaseInsensitive
);
138 bool foundAtLeastOneFile
= false;
145 QByteArray line
= data
.readLine();
149 filePath
[0] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line
.constData(), line
.size()).trimmed()));
150 filePath
[1] = QString(QDir::fromNativeSeparators(codec
->toUnicode(line
.constData(), line
.size()).trimmed()));
151 filePath
[2] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line
.constData(), line
.size()).trimmed()));
155 filePath
[0] = QString(QDir::fromNativeSeparators(codec
->toUnicode(line
.constData(), line
.size()).trimmed()));
156 filePath
[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line
.constData(), line
.size()).trimmed()));
157 filePath
[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line
.constData(), line
.size()).trimmed()));
160 for(size_t i
= 0; i
< 3; i
++)
162 if(!(filePath
[i
].isEmpty() || filePath
[i
].startsWith("#") || filePath
[i
].contains(QChar(QChar::ReplacementCharacter
))))
164 QFileInfo
filename(filePath
[i
]);
165 filename
.setCaching(false);
166 fixFilePath(filename
, baseDir
, rootDir
);
168 if(filename
.exists() && filename
.isFile())
170 qDebug("Found: \"%s\"", filePath
[i
].toUtf8().constData());
171 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
173 fileList
<< filename
.canonicalFilePath();
175 foundAtLeastOneFile
= true;
182 //If we did not find any files yet, try UTF-16 now
183 if(!foundAtLeastOneFile
)
185 const char* codecs
[2] = {"UTF-16LE", "UTF-16BE"};
187 for(size_t i
= 0; i
< 2; i
++)
189 QTextStream
stream(&data
);
190 stream
.setAutoDetectUnicode(false);
191 stream
.setCodec(codecs
[i
]);
194 while(!stream
.atEnd())
196 QString filePath
= stream
.readLine().trimmed();
198 if(!(filePath
.isEmpty() || filePath
.startsWith("#") || filePath
.contains(QChar(QChar::ReplacementCharacter
))))
200 QFileInfo
filename(filePath
);
201 filename
.setCaching(false);
202 fixFilePath(filename
, baseDir
, rootDir
);
204 if(filename
.exists() && filename
.isFile())
206 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
208 fileList
<< filename
.canonicalFilePath();
210 foundAtLeastOneFile
= true;
215 if(foundAtLeastOneFile
) break;
219 return foundAtLeastOneFile
;
222 bool PlaylistImporter::parsePlaylist_pls(QFile
&data
, QStringList
&fileList
, const QDir
&baseDir
, const QDir
&rootDir
)
224 QRegExp
plsEntry("File(\\d+)=(.+)", Qt::CaseInsensitive
);
225 const QTextCodec
*codec
= QTextCodec::codecForName("System");
226 bool foundAtLeastOneFile
= false;
233 QByteArray line
= data
.readLine();
235 filePath
[0] = QString(QDir::fromNativeSeparators(codec
->toUnicode(line
.constData(), line
.size()).trimmed()));
236 filePath
[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line
.constData(), line
.size()).trimmed()));
237 filePath
[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line
.constData(), line
.size()).trimmed()));
239 for(size_t i
= 0; i
< 3; i
++)
241 if(!filePath
[i
].contains(QChar(QChar::ReplacementCharacter
)))
243 if(plsEntry
.indexIn(filePath
[i
]) >= 0)
245 QFileInfo
filename(QDir::fromNativeSeparators(plsEntry
.cap(2)).trimmed());
246 filename
.setCaching(false);
247 fixFilePath(filename
, baseDir
, rootDir
);
249 if(filename
.exists() && filename
.isFile())
251 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
253 fileList
<< filename
.canonicalFilePath();
255 foundAtLeastOneFile
= true;
263 //If we did not find any files yet, try UTF-16 now
264 if(!foundAtLeastOneFile
)
266 const char* codecs
[2] = {"UTF-16LE", "UTF-16BE"};
268 for(size_t i
= 0; i
< 2; i
++)
270 QTextStream
stream(&data
);
271 stream
.setAutoDetectUnicode(false);
272 stream
.setCodec(codecs
[i
]);
275 while(!stream
.atEnd())
277 QString filePath
= stream
.readLine().trimmed();
279 if(!filePath
.contains(QChar(QChar::ReplacementCharacter
)))
281 if(plsEntry
.indexIn(filePath
) >= 0)
283 QFileInfo
filename(QDir::fromNativeSeparators(plsEntry
.cap(2)).trimmed());
284 filename
.setCaching(false);
285 fixFilePath(filename
, baseDir
, rootDir
);
287 if(filename
.exists() && filename
.isFile())
289 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
291 fileList
<< filename
.canonicalFilePath();
293 foundAtLeastOneFile
= true;
299 if(foundAtLeastOneFile
) break;
303 return foundAtLeastOneFile
;
306 bool PlaylistImporter::parsePlaylist_wpl(QFile
&data
, QStringList
&fileList
, const QDir
&baseDir
, const QDir
&rootDir
)
308 bool foundAtLeastOneFile
= false;
310 QRegExp
skipData("<!--(.+)-->", Qt::CaseInsensitive
);
311 QRegExp
wplEntry("<(media|ref)[^<>]*(src|href)=\"([^\"]+)\"[^<>]*>", Qt::CaseInsensitive
);
313 skipData
.setMinimal(true);
315 QByteArray buffer
= data
.readAll();
316 QString line
= QString::fromUtf8(buffer
.constData(), buffer
.size()).simplified();
321 while((index
= skipData
.indexIn(line
)) >= 0)
323 line
.remove(index
, skipData
.matchedLength());
328 while((offset
= wplEntry
.indexIn(line
, offset
) + 1) > 0)
330 QFileInfo
filename(QDir::fromNativeSeparators(unescapeXml(wplEntry
.cap(3)).trimmed()));
331 filename
.setCaching(false);
332 fixFilePath(filename
, baseDir
, rootDir
);
334 if(filename
.exists() && filename
.isFile())
336 if(isPlaylist(filename
.canonicalFilePath()) == notPlaylist
)
338 fileList
<< filename
.canonicalFilePath();
339 foundAtLeastOneFile
= true;
344 return foundAtLeastOneFile
;
347 void PlaylistImporter::fixFilePath(QFileInfo
&filename
, const QDir
&baseDir
, const QDir
&rootDir
)
349 if(filename
.filePath().startsWith("/"))
351 while(filename
.filePath().startsWith("/"))
353 filename
.setFile(filename
.filePath().mid(1));
355 filename
.setFile(rootDir
.filePath(filename
.filePath()));
358 if(!filename
.isAbsolute())
360 filename
.setFile(baseDir
.filePath(filename
.filePath()));
364 QString
&PlaylistImporter::unescapeXml(QString
&str
)
366 for(int i
= 0; (g_xmlEscapeSequence
[i
].escape
&& g_xmlEscapeSequence
[i
].output
); i
++)
368 str
.replace(g_xmlEscapeSequence
[i
].escape
, g_xmlEscapeSequence
[i
].output
);