Adapt for latest MUtils changes.
[LameXP.git] / src / PlaylistImporter.cpp
blob19ee827e19ff261f90f9d3b3cceef9ca300a2f8d
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2016 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 //Internal
26 #include "Global.h"
28 //MUtils
29 #include <MUtils/Global.h>
31 //Qt
32 #include <QString>
33 #include <QStringList>
34 #include <QDir>
35 #include <QFileInfo>
36 #include <QProcess>
37 #include <QDate>
38 #include <QTime>
39 #include <QDebug>
40 #include <QTextCodec>
42 //Un-escape XML characters
43 static const struct
45 char *escape;
46 char *output;
48 g_xmlEscapeSequence[] =
50 {"&amp;", "&"},
51 {"&lt;", "<"},
52 {"&gt;", ">"},
53 {"&apos;", "'"},
54 {"&nbsp;", " "},
55 {"&quot;", "\""},
56 {NULL, NULL}
59 ////////////////////////////////////////////////////////////
60 // Public Functions
61 ////////////////////////////////////////////////////////////
63 bool PlaylistImporter::importPlaylist(QStringList &fileList, const QString &playlistFile)
65 QFileInfo file(playlistFile);
66 QDir baseDir(file.canonicalPath());
68 QDir rootDir(baseDir);
69 while(rootDir.cdUp());
71 //Detect playlist type
72 playlist_t playlistType = isPlaylist(file.canonicalFilePath());
74 //Exit if not a playlist
75 if(playlistType == notPlaylist)
77 return false;
80 QFile data(playlistFile);
82 //Open file for reading
83 if(!data.open(QIODevice::ReadOnly))
85 return false;
88 //Skip very large files (parsing could take very long)
89 if((file.size() < 3) || (file.size() > 524288))
91 qWarning("File is very big. Probably not a Playlist. Rejecting...");
92 return false;
95 //Parse playlist depending on type
96 switch(playlistType)
98 case m3uPlaylist:
99 return parsePlaylist_m3u(data, fileList, baseDir, rootDir);
100 break;
101 case plsPlaylist:
102 return parsePlaylist_pls(data, fileList, baseDir, rootDir);
103 break;
104 case wplPlaylist:
105 return parsePlaylist_wpl(data, fileList, baseDir, rootDir);
106 break;
107 default:
108 return false;
109 break;
113 ////////////////////////////////////////////////////////////
114 // Private Functions
115 ////////////////////////////////////////////////////////////
117 PlaylistImporter::playlist_t PlaylistImporter::isPlaylist(const QString &fileName)
119 QFileInfo file (fileName);
121 if(file.suffix().compare("m3u", Qt::CaseInsensitive) == 0 || file.suffix().compare("m3u8", Qt::CaseInsensitive) == 0)
123 return m3uPlaylist;
125 else if(file.suffix().compare("pls", Qt::CaseInsensitive) == 0)
127 return plsPlaylist;
129 else if(file.suffix().compare("asx", Qt::CaseInsensitive) == 0 || file.suffix().compare("wpl", Qt::CaseInsensitive) == 0)
131 return wplPlaylist;
133 else
135 return notPlaylist;
139 bool PlaylistImporter::parsePlaylist_m3u(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
141 const QTextCodec *codec = QTextCodec::codecForName("System");
142 const bool preferUTF8 = data.fileName().endsWith(".m3u8", Qt::CaseInsensitive);
143 bool foundAtLeastOneFile = false;
145 data.reset();
147 while(!data.atEnd())
149 QString filePath[3];
150 QByteArray line = data.readLine();
152 if(preferUTF8)
154 filePath[0] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
155 filePath[1] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
156 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
158 else
160 filePath[0] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
161 filePath[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
162 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
165 for(size_t i = 0; i < 3; i++)
167 if(!(filePath[i].isEmpty() || filePath[i].startsWith("#") || filePath[i].contains(QChar(QChar::ReplacementCharacter))))
169 QFileInfo filename(filePath[i]);
170 filename.setCaching(false);
171 fixFilePath(filename, baseDir, rootDir);
173 if(filename.exists() && filename.isFile())
175 qDebug("Found: \"%s\"", MUTILS_UTF8(filePath[i]));
176 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
178 fileList << filename.canonicalFilePath();
180 foundAtLeastOneFile = true;
181 break;
187 //If we did not find any files yet, try UTF-16 now
188 if(!foundAtLeastOneFile)
190 const char* codecs[2] = {"UTF-16LE", "UTF-16BE"};
192 for(size_t i = 0; i < 2; i++)
194 QTextStream stream(&data);
195 stream.setAutoDetectUnicode(false);
196 stream.setCodec(codecs[i]);
197 stream.seek(0i64);
199 while(!stream.atEnd())
201 QString filePath = stream.readLine().trimmed();
203 if(!(filePath.isEmpty() || filePath.startsWith("#") || filePath.contains(QChar(QChar::ReplacementCharacter))))
205 QFileInfo filename(filePath);
206 filename.setCaching(false);
207 fixFilePath(filename, baseDir, rootDir);
209 if(filename.exists() && filename.isFile())
211 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
213 fileList << filename.canonicalFilePath();
215 foundAtLeastOneFile = true;
220 if(foundAtLeastOneFile) break;
224 return foundAtLeastOneFile;
227 bool PlaylistImporter::parsePlaylist_pls(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
229 QRegExp plsEntry("File(\\d+)=(.+)", Qt::CaseInsensitive);
230 const QTextCodec *codec = QTextCodec::codecForName("System");
231 bool foundAtLeastOneFile = false;
233 data.reset();
235 while(!data.atEnd())
237 QString filePath[3];
238 QByteArray line = data.readLine();
240 filePath[0] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
241 filePath[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
242 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
244 for(size_t i = 0; i < 3; i++)
246 if(!filePath[i].contains(QChar(QChar::ReplacementCharacter)))
248 if(plsEntry.indexIn(filePath[i]) >= 0)
250 QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed());
251 filename.setCaching(false);
252 fixFilePath(filename, baseDir, rootDir);
254 if(filename.exists() && filename.isFile())
256 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
258 fileList << filename.canonicalFilePath();
260 foundAtLeastOneFile = true;
261 break;
268 //If we did not find any files yet, try UTF-16 now
269 if(!foundAtLeastOneFile)
271 const char* codecs[2] = {"UTF-16LE", "UTF-16BE"};
273 for(size_t i = 0; i < 2; i++)
275 QTextStream stream(&data);
276 stream.setAutoDetectUnicode(false);
277 stream.setCodec(codecs[i]);
278 stream.seek(0i64);
280 while(!stream.atEnd())
282 QString filePath = stream.readLine().trimmed();
284 if(!filePath.contains(QChar(QChar::ReplacementCharacter)))
286 if(plsEntry.indexIn(filePath) >= 0)
288 QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed());
289 filename.setCaching(false);
290 fixFilePath(filename, baseDir, rootDir);
292 if(filename.exists() && filename.isFile())
294 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
296 fileList << filename.canonicalFilePath();
298 foundAtLeastOneFile = true;
304 if(foundAtLeastOneFile) break;
308 return foundAtLeastOneFile;
311 bool PlaylistImporter::parsePlaylist_wpl(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
313 bool foundAtLeastOneFile = false;
315 QRegExp skipData("<!--(.+)-->", Qt::CaseInsensitive);
316 QRegExp wplEntry("<(media|ref)[^<>]*(src|href)=\"([^\"]+)\"[^<>]*>", Qt::CaseInsensitive);
318 skipData.setMinimal(true);
320 QByteArray buffer = data.readAll();
321 QString line = QString::fromUtf8(buffer.constData(), buffer.size()).simplified();
322 buffer.clear();
324 int index = 0;
326 while((index = skipData.indexIn(line)) >= 0)
328 line.remove(index, skipData.matchedLength());
331 int offset = 0;
333 while((offset = wplEntry.indexIn(line, offset) + 1) > 0)
335 QFileInfo filename(QDir::fromNativeSeparators(unescapeXml(wplEntry.cap(3)).trimmed()));
336 filename.setCaching(false);
337 fixFilePath(filename, baseDir, rootDir);
339 if(filename.exists() && filename.isFile())
341 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
343 fileList << filename.canonicalFilePath();
344 foundAtLeastOneFile = true;
349 return foundAtLeastOneFile;
352 void PlaylistImporter::fixFilePath(QFileInfo &filename, const QDir &baseDir, const QDir &rootDir)
354 if(filename.filePath().startsWith("/"))
356 while(filename.filePath().startsWith("/"))
358 filename.setFile(filename.filePath().mid(1));
360 filename.setFile(rootDir.filePath(filename.filePath()));
363 if(!filename.isAbsolute())
365 filename.setFile(baseDir.filePath(filename.filePath()));
369 QString &PlaylistImporter::unescapeXml(QString &str)
371 for(int i = 0; (g_xmlEscapeSequence[i].escape && g_xmlEscapeSequence[i].output); i++)
373 str.replace(g_xmlEscapeSequence[i].escape, g_xmlEscapeSequence[i].output);
376 return str;
379 const char *const *const PlaylistImporter::getSupportedExtensions(void)
381 static const char *const s_supportedExtensions[] =
383 "m3u", "m3u8", "pls", "asx", "wpl", NULL
386 return s_supportedExtensions;