Bump version.
[LameXP.git] / src / PlaylistImporter.cpp
blob10652257dbb3bb25acd20f1a30ed160f3a201668
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.
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"
24 #include <QString>
25 #include <QStringList>
26 #include <QDir>
27 #include <QFileInfo>
28 #include <QProcess>
29 #include <QDate>
30 #include <QTime>
31 #include <QDebug>
32 #include <QTextCodec>
34 //Un-escape XML characters
35 static const struct
37 char *escape;
38 char *output;
40 g_xmlEscapeSequence[] =
42 {"&amp;", "&"},
43 {"&lt;", "<"},
44 {"&gt;", ">"},
45 {"&apos;", "'"},
46 {"&nbsp;", " "},
47 {"&quot;", "\""},
48 {NULL, NULL}
51 const char *PlaylistImporter::supportedExtensions = "*.m3u *.m3u8 *.pls *.asx *.wpl";
54 ////////////////////////////////////////////////////////////
55 // Public Functions
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 //Sanity check
67 if(file.size() < 3 || file.size() > 512000)
69 return false;
72 //Detect playlist type
73 playlist_t playlistType = isPlaylist(file.canonicalFilePath());
75 //Exit if not a playlist
76 if(playlistType == notPlaylist)
78 return false;
81 QFile data(playlistFile);
83 //Open file for reading
84 if(!data.open(QIODevice::ReadOnly))
86 return false;
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...");
93 return false;
96 //Parse playlist depending on type
97 switch(playlistType)
99 case m3uPlaylist:
100 return parsePlaylist_m3u(data, fileList, baseDir, rootDir);
101 break;
102 case plsPlaylist:
103 return parsePlaylist_pls(data, fileList, baseDir, rootDir);
104 break;
105 case wplPlaylist:
106 return parsePlaylist_wpl(data, fileList, baseDir, rootDir);
107 break;
108 default:
109 return false;
110 break;
114 ////////////////////////////////////////////////////////////
115 // Private Functions
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)
124 return m3uPlaylist;
126 else if(file.suffix().compare("pls", Qt::CaseInsensitive) == 0)
128 return plsPlaylist;
130 else if(file.suffix().compare("asx", Qt::CaseInsensitive) == 0 || file.suffix().compare("wpl", Qt::CaseInsensitive) == 0)
132 return wplPlaylist;
134 else
136 return notPlaylist;
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;
146 data.reset();
148 while(!data.atEnd())
150 QString filePath[3];
151 QByteArray line = data.readLine();
153 if(preferUTF8)
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()));
159 else
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;
182 break;
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]);
198 stream.seek(0i64);
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;
225 return true;
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;
234 data.reset();
236 while(!data.atEnd())
238 QString filePath[3];
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;
262 break;
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]);
279 stream.seek(0i64);
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;
309 return true;
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();
321 buffer.clear();
323 int index = 0;
325 while((index = skipData.indexIn(line)) >= 0)
327 line.remove(index, skipData.matchedLength());
330 int offset = 0;
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();
347 return true;
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);
374 return str;