Major redesign of the AudioFileModel class: Split data into separate AudioFileModel_M...
[LameXP.git] / src / PlaylistImporter.cpp
blobf8bab6645c957fc01a0a3510a9ed2f3f7f28a6ae
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2013 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 //Detect playlist type
67 playlist_t playlistType = isPlaylist(file.canonicalFilePath());
69 //Exit if not a playlist
70 if(playlistType == notPlaylist)
72 return false;
75 QFile data(playlistFile);
77 //Open file for reading
78 if(!data.open(QIODevice::ReadOnly))
80 return false;
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...");
87 return false;
90 //Parse playlist depending on type
91 switch(playlistType)
93 case m3uPlaylist:
94 return parsePlaylist_m3u(data, fileList, baseDir, rootDir);
95 break;
96 case plsPlaylist:
97 return parsePlaylist_pls(data, fileList, baseDir, rootDir);
98 break;
99 case wplPlaylist:
100 return parsePlaylist_wpl(data, fileList, baseDir, rootDir);
101 break;
102 default:
103 return false;
104 break;
108 ////////////////////////////////////////////////////////////
109 // Private Functions
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)
118 return m3uPlaylist;
120 else if(file.suffix().compare("pls", Qt::CaseInsensitive) == 0)
122 return plsPlaylist;
124 else if(file.suffix().compare("asx", Qt::CaseInsensitive) == 0 || file.suffix().compare("wpl", Qt::CaseInsensitive) == 0)
126 return wplPlaylist;
128 else
130 return notPlaylist;
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;
140 data.reset();
142 while(!data.atEnd())
144 QString filePath[3];
145 QByteArray line = data.readLine();
147 if(preferUTF8)
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()));
153 else
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;
176 break;
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]);
192 stream.seek(0i64);
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;
228 data.reset();
230 while(!data.atEnd())
232 QString filePath[3];
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;
256 break;
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]);
273 stream.seek(0i64);
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();
317 buffer.clear();
319 int index = 0;
321 while((index = skipData.indexIn(line)) >= 0)
323 line.remove(index, skipData.matchedLength());
326 int offset = 0;
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);
371 return str;