Fixed "mailto:" links in about dialog box.
[LameXP.git] / src / PlaylistImporter.cpp
blob163aa0e57d6c619a7fc41e6c7da6a3df1b8d00af
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2014 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, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
23 #include "PlaylistImporter.h"
25 #include "Global.h"
27 #include <QString>
28 #include <QStringList>
29 #include <QDir>
30 #include <QFileInfo>
31 #include <QProcess>
32 #include <QDate>
33 #include <QTime>
34 #include <QDebug>
35 #include <QTextCodec>
37 //Un-escape XML characters
38 static const struct
40 char *escape;
41 char *output;
43 g_xmlEscapeSequence[] =
45 {"&amp;", "&"},
46 {"&lt;", "<"},
47 {"&gt;", ">"},
48 {"&apos;", "'"},
49 {"&nbsp;", " "},
50 {"&quot;", "\""},
51 {NULL, NULL}
54 const char *PlaylistImporter::supportedExtensions = "*.m3u *.m3u8 *.pls *.asx *.wpl";
57 ////////////////////////////////////////////////////////////
58 // Public Functions
59 ////////////////////////////////////////////////////////////
61 bool PlaylistImporter::importPlaylist(QStringList &fileList, const QString &playlistFile)
63 QFileInfo file(playlistFile);
64 QDir baseDir(file.canonicalPath());
66 QDir rootDir(baseDir);
67 while(rootDir.cdUp());
69 //Detect playlist type
70 playlist_t playlistType = isPlaylist(file.canonicalFilePath());
72 //Exit if not a playlist
73 if(playlistType == notPlaylist)
75 return false;
78 QFile data(playlistFile);
80 //Open file for reading
81 if(!data.open(QIODevice::ReadOnly))
83 return false;
86 //Skip very large files (parsing could take very long)
87 if((file.size() < 3) || (file.size() > 524288))
89 qWarning("File is very big. Probably not a Playlist. Rejecting...");
90 return false;
93 //Parse playlist depending on type
94 switch(playlistType)
96 case m3uPlaylist:
97 return parsePlaylist_m3u(data, fileList, baseDir, rootDir);
98 break;
99 case plsPlaylist:
100 return parsePlaylist_pls(data, fileList, baseDir, rootDir);
101 break;
102 case wplPlaylist:
103 return parsePlaylist_wpl(data, fileList, baseDir, rootDir);
104 break;
105 default:
106 return false;
107 break;
111 ////////////////////////////////////////////////////////////
112 // Private Functions
113 ////////////////////////////////////////////////////////////
115 PlaylistImporter::playlist_t PlaylistImporter::isPlaylist(const QString &fileName)
117 QFileInfo file (fileName);
119 if(file.suffix().compare("m3u", Qt::CaseInsensitive) == 0 || file.suffix().compare("m3u8", Qt::CaseInsensitive) == 0)
121 return m3uPlaylist;
123 else if(file.suffix().compare("pls", Qt::CaseInsensitive) == 0)
125 return plsPlaylist;
127 else if(file.suffix().compare("asx", Qt::CaseInsensitive) == 0 || file.suffix().compare("wpl", Qt::CaseInsensitive) == 0)
129 return wplPlaylist;
131 else
133 return notPlaylist;
137 bool PlaylistImporter::parsePlaylist_m3u(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
139 const QTextCodec *codec = QTextCodec::codecForName("System");
140 const bool preferUTF8 = data.fileName().endsWith(".m3u8", Qt::CaseInsensitive);
141 bool foundAtLeastOneFile = false;
143 data.reset();
145 while(!data.atEnd())
147 QString filePath[3];
148 QByteArray line = data.readLine();
150 if(preferUTF8)
152 filePath[0] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
153 filePath[1] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
154 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
156 else
158 filePath[0] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
159 filePath[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
160 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
163 for(size_t i = 0; i < 3; i++)
165 if(!(filePath[i].isEmpty() || filePath[i].startsWith("#") || filePath[i].contains(QChar(QChar::ReplacementCharacter))))
167 QFileInfo filename(filePath[i]);
168 filename.setCaching(false);
169 fixFilePath(filename, baseDir, rootDir);
171 if(filename.exists() && filename.isFile())
173 qDebug("Found: \"%s\"", QUTF8(filePath[i]));
174 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
176 fileList << filename.canonicalFilePath();
178 foundAtLeastOneFile = true;
179 break;
185 //If we did not find any files yet, try UTF-16 now
186 if(!foundAtLeastOneFile)
188 const char* codecs[2] = {"UTF-16LE", "UTF-16BE"};
190 for(size_t i = 0; i < 2; i++)
192 QTextStream stream(&data);
193 stream.setAutoDetectUnicode(false);
194 stream.setCodec(codecs[i]);
195 stream.seek(0i64);
197 while(!stream.atEnd())
199 QString filePath = stream.readLine().trimmed();
201 if(!(filePath.isEmpty() || filePath.startsWith("#") || filePath.contains(QChar(QChar::ReplacementCharacter))))
203 QFileInfo filename(filePath);
204 filename.setCaching(false);
205 fixFilePath(filename, baseDir, rootDir);
207 if(filename.exists() && filename.isFile())
209 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
211 fileList << filename.canonicalFilePath();
213 foundAtLeastOneFile = true;
218 if(foundAtLeastOneFile) break;
222 return foundAtLeastOneFile;
225 bool PlaylistImporter::parsePlaylist_pls(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
227 QRegExp plsEntry("File(\\d+)=(.+)", Qt::CaseInsensitive);
228 const QTextCodec *codec = QTextCodec::codecForName("System");
229 bool foundAtLeastOneFile = false;
231 data.reset();
233 while(!data.atEnd())
235 QString filePath[3];
236 QByteArray line = data.readLine();
238 filePath[0] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
239 filePath[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
240 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
242 for(size_t i = 0; i < 3; i++)
244 if(!filePath[i].contains(QChar(QChar::ReplacementCharacter)))
246 if(plsEntry.indexIn(filePath[i]) >= 0)
248 QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed());
249 filename.setCaching(false);
250 fixFilePath(filename, baseDir, rootDir);
252 if(filename.exists() && filename.isFile())
254 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
256 fileList << filename.canonicalFilePath();
258 foundAtLeastOneFile = true;
259 break;
266 //If we did not find any files yet, try UTF-16 now
267 if(!foundAtLeastOneFile)
269 const char* codecs[2] = {"UTF-16LE", "UTF-16BE"};
271 for(size_t i = 0; i < 2; i++)
273 QTextStream stream(&data);
274 stream.setAutoDetectUnicode(false);
275 stream.setCodec(codecs[i]);
276 stream.seek(0i64);
278 while(!stream.atEnd())
280 QString filePath = stream.readLine().trimmed();
282 if(!filePath.contains(QChar(QChar::ReplacementCharacter)))
284 if(plsEntry.indexIn(filePath) >= 0)
286 QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed());
287 filename.setCaching(false);
288 fixFilePath(filename, baseDir, rootDir);
290 if(filename.exists() && filename.isFile())
292 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
294 fileList << filename.canonicalFilePath();
296 foundAtLeastOneFile = true;
302 if(foundAtLeastOneFile) break;
306 return foundAtLeastOneFile;
309 bool PlaylistImporter::parsePlaylist_wpl(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
311 bool foundAtLeastOneFile = false;
313 QRegExp skipData("<!--(.+)-->", Qt::CaseInsensitive);
314 QRegExp wplEntry("<(media|ref)[^<>]*(src|href)=\"([^\"]+)\"[^<>]*>", Qt::CaseInsensitive);
316 skipData.setMinimal(true);
318 QByteArray buffer = data.readAll();
319 QString line = QString::fromUtf8(buffer.constData(), buffer.size()).simplified();
320 buffer.clear();
322 int index = 0;
324 while((index = skipData.indexIn(line)) >= 0)
326 line.remove(index, skipData.matchedLength());
329 int offset = 0;
331 while((offset = wplEntry.indexIn(line, offset) + 1) > 0)
333 QFileInfo filename(QDir::fromNativeSeparators(unescapeXml(wplEntry.cap(3)).trimmed()));
334 filename.setCaching(false);
335 fixFilePath(filename, baseDir, rootDir);
337 if(filename.exists() && filename.isFile())
339 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
341 fileList << filename.canonicalFilePath();
342 foundAtLeastOneFile = true;
347 return foundAtLeastOneFile;
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;